Skip to content

Commit

Permalink
Hash the preview param
Browse files Browse the repository at this point in the history
Resolves #15605
  • Loading branch information
brandonkelly committed Aug 27, 2024
1 parent 838d3ec commit f82baa7
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@

### System
- MySQL mutex locks and PHP session names are now namespaced using the application ID combined with the environment name. ([#15313](https://github.com/craftcms/cms/issues/15313))
- `x-craft-preview` and `x-craft-live-preview` params are now hashed, and `craft\web\Request::getIsPreview()` will only return `true` if the param validates. ([#15605](https://github.com/craftcms/cms/discussions/15605))
6 changes: 5 additions & 1 deletion src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use craft\helpers\Db;
use craft\helpers\ElementHelper;
use craft\helpers\Html;
use craft\helpers\StringHelper;
use craft\helpers\UrlHelper;
use craft\models\ElementActivity;
use craft\models\FieldLayoutForm;
Expand Down Expand Up @@ -440,6 +441,7 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null):
'isUnpublishedDraft' => $isUnpublishedDraft,
'previewTargets' => $previewTargets,
'previewToken' => $previewTargets ? $security->generateRandomString() : null,
'previewParamValue' => $previewTargets ? $security->hashData(StringHelper::randomString(10)) : null,
'revisionId' => $element->revisionId,
'siteId' => $element->siteId,
'siteStatuses' => $siteStatuses,
Expand Down Expand Up @@ -1423,11 +1425,13 @@ public function actionSaveDraft(): ?Response

if ($this->request->getIsCpRequest()) {
[$docTitle, $title] = $this->_editElementTitles($element);
$previewTargets = $element->getPreviewTargets();
$data += $this->_fieldLayoutData($element);
$data += [
'docTitle' => $docTitle,
'title' => $title,
'previewTargets' => $element->getPreviewTargets(),
'previewTargets' => $previewTargets,
'previewParamValue' => $previewTargets ? Craft::$app->getSecurity()->hashData(StringHelper::randomString(10)) : null,
'initialDeltaValues' => Craft::$app->getView()->getInitialDeltaValues(),
'updatedTimestamp' => $element->dateUpdated->getTimestamp(),
'canonicalUpdatedTimestamp' => $element->getCanonical()->dateUpdated->getTimestamp(),
Expand Down
15 changes: 10 additions & 5 deletions src/web/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -696,11 +696,16 @@ public function getActionSegments(): ?array
*/
public function getIsPreview(): bool
{
return (
($this->getQueryParam('x-craft-preview') ?? $this->getQueryParam('x-craft-live-preview')) !== null &&
// If there's a token but it expired, they're looking at the live site
(!$this->getHadToken() || $this->getToken() !== null)
);
$previewParamValue = $this->getQueryParam('x-craft-preview') ?? $this->getQueryParam('x-craft-live-preview');
if (!$previewParamValue) {
return false;
}
if (!Craft::$app->getSecurity()->validateData($previewParamValue)) {
return false;
}

// If there's a token but it expired, they're looking at the live site
return !$this->getHadToken() || $this->getToken() !== null;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions src/web/assets/cp/src/js/ElementEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -923,20 +923,20 @@ Craft.ElementEditor = Garnish.Base.extend(

/**
* @param {string} url
* @param {?string} [randoParam]
* @param {boolean} [asPromise=false]
* @param {?string} [previewParam]
* @param {boolean} [asPromise=true]
* @returns {(Promise|string)}
*/
getTokenizedPreviewUrl: function (url, randoParam, asPromise) {
if (typeof asPromise === 'undefined') {
asPromise = true;
}

getTokenizedPreviewUrl: function (url, previewParam, asPromise = true) {
const params = {};

if (randoParam || !this.settings.isLive) {
if (
this.settings.previewParamValue &&
(previewParam || !this.settings.isLive)
) {
// Randomize the URL so CDNs don't return cached pages
params[randoParam || 'x-craft-preview'] = Craft.randomString(10);
params[previewParam || 'x-craft-preview'] =
this.settings.previewParamValue;
}

if (this.settings.siteToken) {
Expand Down Expand Up @@ -1235,6 +1235,7 @@ Craft.ElementEditor = Garnish.Base.extend(
})
.then((response) => {
this._afterSaveDraft();
this.settings.previewParamValue = response.data.previewParamValue;
this._afterUpdateFieldLayout(data, selectedTabId, response);

const createdProvisionalDraft = !this.settings.draftId;
Expand Down Expand Up @@ -2253,6 +2254,7 @@ Craft.ElementEditor = Garnish.Base.extend(
isUnpublishedDraft: false,
previewTargets: [],
previewToken: null,
previewParamValue: null,
revisionId: null,
siteId: null,
siteStatuses: null,
Expand Down

0 comments on commit f82baa7

Please sign in to comment.