Commit 29d5e929 authored by jrockowitz's avatar jrockowitz

Issue #3082822: Anonymous role view submission via secure token

parent 68c93a6f
......@@ -431,9 +431,12 @@
results_disabled_ignore:
type: boolean
label: 'Ignore disabled results warning'
token_view:
type: boolean
label: 'Allow viewing a submission using token'
token_update:
type: boolean
label: 'Allow updates using token'
label: 'Allow updating a submission using token'
access:
type: sequence
label: 'Access Rules'
......
......@@ -325,9 +325,14 @@ function template_preprocess_webform_submission_information(array &$variables) {
$variables['uri'] = Link::fromTextAndUrl($source_url->setAbsolute(FALSE)->toString(), $source_url);
}
if ($webform->getSetting('token_view')) {
$token_view_url = $webform_submission->getTokenUrl('view');
$variables['token_view'] = Link::fromTextAndUrl($token_view_url->setAbsolute(FALSE)->toString(), $token_view_url);
}
if ($webform->getSetting('token_update')) {
$token_url = $webform_submission->getTokenUrl();
$variables['token_update'] = Link::fromTextAndUrl($token_url->setAbsolute(FALSE)->toString(), $token_url);
$token_update_url = $webform_submission->getTokenUrl('update');
$variables['token_update'] = Link::fromTextAndUrl($token_update_url->setAbsolute(FALSE)->toString(), $token_update_url);
}
if (($source_entity = $webform_submission->getSourceEntity()) && $source_entity->hasLinkTemplate('canonical')) {
......
......@@ -1055,6 +1055,7 @@ class Webform extends ConfigEntityBundleBase implements WebformInterface {
'purge_days' => NULL,
'results_disabled' => FALSE,
'results_disabled_ignore' => FALSE,
'token_view' => FALSE,
'token_update' => FALSE,
];
}
......@@ -1937,7 +1938,7 @@ class Webform extends ConfigEntityBundleBase implements WebformInterface {
$cache_contexts[] = 'url.query_args:entity_id';
}
// Add webform (secure) token query string parameter.
if ($this->getSetting('token_update')) {
if ($this->getSetting('token_view') || $this->getSetting('token_update')) {
$cache_contexts[] = 'url.query_args:token';
}
}
......
......@@ -494,8 +494,22 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
/**
* {@inheritdoc}
*/
public function getTokenUrl() {
$uri = $this->getSourceUrl();
public function getTokenUrl($operation = 'update') {
switch ($operation) {
case 'view':
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
$request_handler = \Drupal::service('webform.request');
$uri = $request_handler->getUrl($this, $this->getSourceEntity(), 'webform.user.submission');
break;
case 'update':
$uri = $this->getSourceUrl();
break;
default:
throw new \Exception("Token URL operation $operation is not supported");
}
$options = $uri->getOptions();
$options['query']['token'] = $this->getToken();
return $uri->setOptions($options);
......
......@@ -200,6 +200,11 @@ class WebformEntitySettingsSubmissionsForm extends WebformEntitySettingsBaseForm
'title' => $this->t('Show the notification about previous submissions'),
'form_description' => $this->t('Show the previous submissions notification that appears when users have previously submitted this form.'),
],
'token_view' => [
'title' => $this->t('Allow users to view a submission using a secure token'),
'form_description' => $this->t("If checked users will be able to view a submission using the webform submission's URL appended with the submission's (secure) token.") . ' ' .
$this->t("The 'tokenized' URL to view a submission will be available when viewing a submission's information and can be inserted into an email using the [webform_submission:view-url] token."),
],
'token_update' => [
'title' => $this->t('Allow users to update a submission using a secure token'),
'form_description' => $this->t("If checked users will be able to update a submission using the webform's URL appended with the submission's (secure) token.") . ' ' .
......@@ -221,7 +226,9 @@ class WebformEntitySettingsSubmissionsForm extends WebformEntitySettingsBaseForm
'#message_message' => $this->t("Submissions accessed using the (secure) token will by-pass all webform submission access rules."),
'#states' => [
'visible' => [
':input[name="token_update"]' => ['checked' => TRUE],
[':input[name="token_view"]' => ['checked' => TRUE]],
'or',
[':input[name="token_update"]' => ['checked' => TRUE]],
],
],
'#weight' => $form['submission_behaviors']['token_update']['#weight'] + 1,
......
......@@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\webform\Access\WebformAccessResult;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Defines the access control handler for the webform submission entity type.
......@@ -24,6 +25,13 @@ class WebformSubmissionAccessControlHandler extends EntityAccessControlHandler i
*/
protected $accessRulesManager;
/**
* The current request.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* WebformSubmissionAccessControlHandler constructor.
*
......@@ -31,11 +39,14 @@ class WebformSubmissionAccessControlHandler extends EntityAccessControlHandler i
* The entity type definition.
* @param \Drupal\webform\WebformAccessRulesManagerInterface $access_rules_manager
* Webform access rules manager service.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
*/
public function __construct(EntityTypeInterface $entity_type, WebformAccessRulesManagerInterface $access_rules_manager) {
public function __construct(EntityTypeInterface $entity_type, WebformAccessRulesManagerInterface $access_rules_manager, RequestStack $request_stack) {
parent::__construct($entity_type);
$this->accessRulesManager = $access_rules_manager;
$this->request = $request_stack->getCurrentRequest();
}
/**
......@@ -44,7 +55,8 @@ class WebformSubmissionAccessControlHandler extends EntityAccessControlHandler i
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('webform.access_rules_manager')
$container->get('webform.access_rules_manager'),
$container->get('request_stack')
);
}
......@@ -69,6 +81,17 @@ class WebformSubmissionAccessControlHandler extends EntityAccessControlHandler i
return WebformAccessResult::allowed($entity, TRUE);
}
// Check view operation token access.
if ($operation === 'view'
&& $entity->getWebform()->getSetting('token_view')) {
$token = $this->request->query->get('token');
if ($token === $entity->getToken()) {
return WebformAccessResult::allowed($entity)
->addCacheContexts(['url.query_args:token']);
}
}
// Check 'any' or 'own' webform submission permissions.
$operations = [
'view' => 'view',
......
......@@ -388,10 +388,13 @@ interface WebformSubmissionInterface extends ContentEntityInterface, EntityOwner
/**
* Gets the webform submission's secure tokenized URL.
*
* @param string $operation
* Token URL's operation. Defaults to update.
*
* @return \Drupal\Core\Url
* The webform submission's secure tokenized URL.
*/
public function getTokenUrl();
public function getTokenUrl($operation = 'update');
/**
* Invoke all webform handlers method.
......
......@@ -37,6 +37,9 @@
{% if uri %}
<div><b>{{ 'Submission URI'|t }}:</b> {{ uri }}</div>
{% endif %}
{% if token_view %}
<div><b>{{ 'Submission View'|t }}:</b> {{ token_view }}</div>
{% endif %}
{% if token_update %}
<div><b>{{ 'Submission Update'|t }}:</b> {{ token_update }}</div>
{% endif %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment