Skip to content

Commit

Permalink
Merge pull request #6836 from getkirby/v5/changes/preview-tokens-8
Browse files Browse the repository at this point in the history
Preview tokens 8: New structure for preview tokens
  • Loading branch information
bastianallgeier authored Dec 6, 2024
2 parents d205d47 + c452ffc commit 249c269
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 16 deletions.
33 changes: 29 additions & 4 deletions src/Content/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Kirby\Exception\NotFoundException;
use Kirby\Form\Form;
use Kirby\Http\Uri;
use Kirby\Toolkit\Str;

/**
* The Version class handles all actions for a single
Expand Down Expand Up @@ -393,7 +394,7 @@ protected function prepareFieldsForContent(

/**
* Returns a verification token for the authentication
* of draft previews
* of draft and version previews
* @internal
*/
public function previewToken(): string
Expand All @@ -413,10 +414,34 @@ public function previewToken(): string
throw new LogicException('Invalid model type');
}

return $this->model->kirby()->contentToken(
$this->model,
$this->model->id() . $this->model->template()
return $this->previewTokenFromUrl($this->model->url())
?? throw new LogicException('Cannot produce local preview token for model');
}

/**
* Returns a verification token for the authentication
* of draft and version previews from a raw URL
* if the URL comes from the same site
*/
protected function previewTokenFromUrl(string $url): string|null
{
$localPrefix = $this->model->kirby()->url('base') . '/';

if (Str::startsWith($url, $localPrefix) === false) {
return null;
}

$data = [
'uri' => Str::after($url, $localPrefix),
'versionId' => $this->id->value()
];

$token = $this->model->kirby()->contentToken(
null,
json_encode($data)
);

return substr($token, 0, 10);
}

/**
Expand Down
30 changes: 30 additions & 0 deletions tests/Cms/Pages/PageRenderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,36 @@ public function testRenderVersionFromRequestAuthenticatedDraft()
$this->assertSame('changes', $page->renderVersionFromRequest()->value());
}

/**
* @covers ::renderVersionFromRequest
*/
public function testRenderVersionFromRequestMismatch()
{
$page = $this->app->page('default');

$this->app->clone([
'request' => [
'query' => [
'_token' => $page->version('changes')->previewToken(),
'_version' => 'latest'
]
]
]);

$this->assertSame('latest', $page->renderVersionFromRequest()->value());

$this->app->clone([
'request' => [
'query' => [
'_token' => $page->version('latest')->previewToken(),
'_version' => 'changes'
]
]
]);

$this->assertSame('latest', $page->renderVersionFromRequest()->value());
}

/**
* @covers ::renderVersionFromRequest
*/
Expand Down
3 changes: 2 additions & 1 deletion tests/Cms/Pages/PageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -677,9 +677,10 @@ public function testCustomPreviewUrl(
]);

if ($draft === true && $expected !== null) {
$expectedToken = substr(hash_hmac('sha1', '{"uri":"' . $page->uri() . '","versionId":"latest"}', $page->kirby()->root('content')), 0, 10);
$expected = str_replace(
'{token}',
'_token=' . hash_hmac('sha1', $page->id() . $page->template(), $page->kirby()->root('content') . '/' . $page->id()),
'_token=' . $expectedToken,
$expected
);
}
Expand Down
45 changes: 34 additions & 11 deletions tests/Content/VersionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -786,14 +786,16 @@ public function testPreviewToken()
model: $this->app->site(),
id: VersionId::latest()
);
$this->assertSame(hash_hmac('sha1', 'home' . 'default', static::TMP . '/content/home'), $version->previewToken());
$expected = substr(hash_hmac('sha1', '{"uri":"","versionId":"latest"}', static::TMP . '/content'), 0, 10);
$this->assertSame($expected, $version->previewToken());

// page
$version = new Version(
model: $this->model,
id: VersionId::latest()
);
$this->assertSame(hash_hmac('sha1', 'a-page' . 'default', static::TMP . '/content/a-page'), $version->previewToken());
$expected = substr(hash_hmac('sha1', '{"uri":"a-page","versionId":"latest"}', static::TMP . '/content'), 0, 10);
$this->assertSame($expected, $version->previewToken());
}

/**
Expand All @@ -816,7 +818,8 @@ public function testPreviewTokenCustomSalt()
id: VersionId::latest()
);

$this->assertSame(hash_hmac('sha1', 'a-page' . 'default', 'testsalt'), $version->previewToken());
$expected = substr(hash_hmac('sha1', '{"uri":"a-page","versionId":"latest"}', 'testsalt'), 0, 10);
$this->assertSame($expected, $version->previewToken());
}

/**
Expand All @@ -829,20 +832,22 @@ public function testPreviewTokenCustomSaltCallback()
$this->app = $this->app->clone([
'options' => [
'content' => [
'salt' => fn ($page) => $page->date()
'salt' => function ($model) {
$this->assertNull($model);

return 'salt-lake-city';
}
]
]
]);

$this->app->impersonate('kirby');
$model = $this->model->update(['date' => '2012-12-12']);

$version = new Version(
model: $model,
model: $this->model,
id: VersionId::latest()
);

$this->assertSame(hash_hmac('sha1', 'a-page' . 'default', '2012-12-12'), $version->previewToken());
$expected = substr(hash_hmac('sha1', '{"uri":"a-page","versionId":"latest"}', 'salt-lake-city'), 0, 10);
$this->assertSame($expected, $version->previewToken());
}

/**
Expand Down Expand Up @@ -1589,9 +1594,18 @@ public function testUrlPageCustom(
]);

if ($expected !== null) {
$expectedToken = substr(
hash_hmac(
'sha1',
'{"uri":"' . $page->uri() . '","versionId":"' . $versionId . '"}',
$page->kirby()->root('content')
),
0,
10
);
$expected = str_replace(
'{token}',
'_token=' . hash_hmac('sha1', $page->id() . $page->template(), $page->kirby()->root('content') . '/' . $page->id()),
'_token=' . $expectedToken,
$expected
);
}
Expand Down Expand Up @@ -1706,9 +1720,18 @@ public function testUrlSiteCustom(
$site = $app->site();

if ($expected !== null) {
$expectedToken = substr(
hash_hmac(
'sha1',
'{"uri":"","versionId":"' . $versionId . '"}',
$site->kirby()->root('content')
),
0,
10
);
$expected = str_replace(
'{token}',
'_token=' . hash_hmac('sha1', 'home' . 'default', $site->kirby()->root('content') . '/home'),
'_token=' . $expectedToken,
$expected
);
}
Expand Down

0 comments on commit 249c269

Please sign in to comment.