From 5a169923985e017cd208eea4491e7dc6c0209b72 Mon Sep 17 00:00:00 2001
From: Franz Liedke
Date: Thu, 13 Jun 2019 01:03:39 +0200
Subject: [PATCH 1/5] Update changelog
---
framework/core/CHANGELOG.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/framework/core/CHANGELOG.md b/framework/core/CHANGELOG.md
index aa6a9e2c0..f6c328f9b 100644
--- a/framework/core/CHANGELOG.md
+++ b/framework/core/CHANGELOG.md
@@ -11,6 +11,7 @@
- Check and enforce minimum MariaDB ([7ff9a90](https://github.com/flarum/core/commit/7ff9a90204923293adc520d3c02dc984845d4f9f))
- Revert publication of assets when installation fails ([ed9591c](https://github.com/flarum/core/commit/ed9591c16fb2ea7a4be3387b805d855a53e0a7d5))
- Benefit from Laravel's database reconnection logic in long-running tasks ([e0becd0](https://github.com/flarum/core/commit/e0becd0c7bda939048923c1f86648793feee78d5))
+- The "vendor path" (where Composer dependencies can be found) can now be configured ([5e1680c](https://github.com/flarum/core/commit/5e1680c458cd3ba274faeb92de3ac2053789131e))
### Changed
- Performance: Actually cache translations on disk ([0d16fac](https://github.com/flarum/core/commit/0d16fac001bb735ee66e82871183516aeac269b7))
@@ -36,9 +37,9 @@
- Entire forum was breaking when the `custom_less` setting was missing from the database ([bf2c5a5](https://github.com/flarum/core/commit/bf2c5a5564dff3f5ef13efe7a8d69f2617570ce6))
- Dropdown icon was not showing in user card when on user page ([12fdfc9](https://github.com/flarum/core/commit/12fdfc9b544a27f6fe59c82ad6bddd3420cc0181))
- Requests were missing the `original*` attributes, which broke installations in subfolders ([56fde28](https://github.com/flarum/core/commit/56fde28e436f52fee0c03c538f0a6049bc584b53))
-- Using `like` in queries would allow underscores to match other users as well ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
-- Font awesome component package changed paths in version 5.9.0 ([5eb69e1](https://github.com/flarum/core/commit/5eb69e1f59fa73fdfd5badbf41a05a6a040e7426))
-- Fixed issue with using the system wide tmp path for storing JS file maps by moving these files into the storage/tmp directory ([54660eb](https://github.com/flarum/core/commit/54660ebd6311f9ea142f1b573263d0d907400786))
+- Special characters such as `%` and `_` could slow down queries ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
+- FontAwesome component package changed paths in version 5.9.0 ([5eb69e1](https://github.com/flarum/core/commit/5eb69e1f59fa73fdfd5badbf41a05a6a040e7426))
+- Some server environments had problems accessing the system-wide tmp path for storing JS file maps ([54660eb](https://github.com/flarum/core/commit/54660ebd6311f9ea142f1b573263d0d907400786))
### Removed
- `php flarum install --defaults` - this was meant to be used in our old development VM ([44c9109](https://github.com/flarum/core/commit/44c91099cd77138bb5fc29f14fb1e81a9781272d))
From 6fe9ea3dee363ce799126802d8c759a6a5187b7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?=
Date: Thu, 13 Jun 2019 09:13:31 +0200
Subject: [PATCH 2/5] Update CHANGELOG.md
clarifying reason for change on the `like` fix
---
framework/core/CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/framework/core/CHANGELOG.md b/framework/core/CHANGELOG.md
index f6c328f9b..4a02cbfb2 100644
--- a/framework/core/CHANGELOG.md
+++ b/framework/core/CHANGELOG.md
@@ -37,7 +37,7 @@
- Entire forum was breaking when the `custom_less` setting was missing from the database ([bf2c5a5](https://github.com/flarum/core/commit/bf2c5a5564dff3f5ef13efe7a8d69f2617570ce6))
- Dropdown icon was not showing in user card when on user page ([12fdfc9](https://github.com/flarum/core/commit/12fdfc9b544a27f6fe59c82ad6bddd3420cc0181))
- Requests were missing the `original*` attributes, which broke installations in subfolders ([56fde28](https://github.com/flarum/core/commit/56fde28e436f52fee0c03c538f0a6049bc584b53))
-- Special characters such as `%` and `_` could slow down queries ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
+- Special characters such as `%` and `_` could return incorrect results ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
- FontAwesome component package changed paths in version 5.9.0 ([5eb69e1](https://github.com/flarum/core/commit/5eb69e1f59fa73fdfd5badbf41a05a6a040e7426))
- Some server environments had problems accessing the system-wide tmp path for storing JS file maps ([54660eb](https://github.com/flarum/core/commit/54660ebd6311f9ea142f1b573263d0d907400786))
From a65074d01bba276bde4619f452df02ace6bd3ce4 Mon Sep 17 00:00:00 2001
From: Franz Liedke
Date: Mon, 24 Jun 2019 09:14:39 +0200
Subject: [PATCH 3/5] Merge pull request from GHSA-3wjh-93gr-chh6
* Integration tests: Memoize request handler as well
This is useful to send HTTP requests (or their PSR-7 equivalents)
through the entire application's middleware stack (instead of
talking to specific controllers, which should be considered
implementation detail).
* Add tests for CSRF token check
* Integration tests: Configure vendor path
Now that this is possible, make the easy change...
* Implement middleware for CSRF token verification
This fixes a rather large oversight in Flarum's codebase, which was that
we had no explicit CSRF protection using the traditional token approach.
The JS frontend was actually sending these tokens, but the backend did
not require them.
* Accept CSRF token in request body as well
* Refactor tests to shorten HTTP requests
Multiple tests now provide JSON request bodies, and others copy cookies
from previous responses, so let's provide convenient helpers for these.
* Fixed issue with tmp/storage/views not existing, this caused tmpname to notice.
Fixed csrf test that assumed an access token allows application access, which is actually api token.
Improved return type hinting in the StartSession middleware
* Using a different setting key now, so that it won't break tests whenever you re-run them once smtp is set.
Fixed, badly, the test to create users etc caused by the prepareDatabase flushing all settings by default.
* added custom view, now needs translation
---
.../core/src/Admin/AdminServiceProvider.php | 1 +
framework/core/src/Api/ApiServiceProvider.php | 1 +
.../core/src/Forum/ForumServiceProvider.php | 1 +
.../Http/Exception/TokenMismatchException.php | 4 +
.../Middleware/AuthenticateWithHeader.php | 1 +
.../src/Http/Middleware/CheckCsrfToken.php | 48 +++++
.../core/src/Http/Middleware/StartSession.php | 8 +-
framework/core/tests/integration/TestCase.php | 122 +++++++++--
.../Controller/CreateUserControllerTest.php | 3 +
.../csrf_protection/RequireCsrfTokenTest.php | 193 ++++++++++++++++++
.../tests/integration/tmp/storage/.gitkeep | 0
framework/core/views/error/419.blade.php | 12 ++
12 files changed, 376 insertions(+), 18 deletions(-)
create mode 100644 framework/core/src/Http/Middleware/CheckCsrfToken.php
create mode 100644 framework/core/tests/integration/api/csrf_protection/RequireCsrfTokenTest.php
delete mode 100644 framework/core/tests/integration/tmp/storage/.gitkeep
create mode 100644 framework/core/views/error/419.blade.php
diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php
index 5e61729bd..493464ba9 100644
--- a/framework/core/src/Admin/AdminServiceProvider.php
+++ b/framework/core/src/Admin/AdminServiceProvider.php
@@ -61,6 +61,7 @@ class AdminServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
+ $pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(Middleware\RequireAdministrateAbility::class));
diff --git a/framework/core/src/Api/ApiServiceProvider.php b/framework/core/src/Api/ApiServiceProvider.php
index eb077259c..dd8eb6cd9 100644
--- a/framework/core/src/Api/ApiServiceProvider.php
+++ b/framework/core/src/Api/ApiServiceProvider.php
@@ -57,6 +57,7 @@ class ApiServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithHeader::class));
+ $pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
event(new ConfigureMiddleware($pipe, 'api'));
diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php
index cdb4e0e48..56edb7132 100644
--- a/framework/core/src/Forum/ForumServiceProvider.php
+++ b/framework/core/src/Forum/ForumServiceProvider.php
@@ -68,6 +68,7 @@ class ForumServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
+ $pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(HttpMiddleware\ShareErrorsFromSession::class));
diff --git a/framework/core/src/Http/Exception/TokenMismatchException.php b/framework/core/src/Http/Exception/TokenMismatchException.php
index 49da0abaa..87871937a 100644
--- a/framework/core/src/Http/Exception/TokenMismatchException.php
+++ b/framework/core/src/Http/Exception/TokenMismatchException.php
@@ -15,4 +15,8 @@ use Exception;
class TokenMismatchException extends Exception
{
+ public function __construct($message = null, $code = 419, Exception $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+ }
}
diff --git a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php
index 787470c90..512f90101 100644
--- a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php
+++ b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php
@@ -40,6 +40,7 @@ class AuthenticateWithHeader implements Middleware
$request = $request->withAttribute('apiKey', $key);
$request = $request->withAttribute('bypassFloodgate', true);
+ $request = $request->withAttribute('bypassCsrfToken', true);
} elseif ($token = AccessToken::find($id)) {
$token->touch();
diff --git a/framework/core/src/Http/Middleware/CheckCsrfToken.php b/framework/core/src/Http/Middleware/CheckCsrfToken.php
new file mode 100644
index 000000000..d2b2d6da8
--- /dev/null
+++ b/framework/core/src/Http/Middleware/CheckCsrfToken.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Flarum\Http\Middleware;
+
+use Flarum\Http\Exception\TokenMismatchException;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Server\MiddlewareInterface as Middleware;
+use Psr\Http\Server\RequestHandlerInterface as Handler;
+
+class CheckCsrfToken implements Middleware
+{
+ public function process(Request $request, Handler $handler): Response
+ {
+ if (in_array($request->getMethod(), ['GET', 'HEAD', 'OPTIONS'])) {
+ return $handler->handle($request);
+ }
+
+ if ($request->getAttribute('bypassCsrfToken', false)) {
+ return $handler->handle($request);
+ }
+
+ if ($this->tokensMatch($request)) {
+ return $handler->handle($request);
+ }
+
+ throw new TokenMismatchException('CSRF token did not match');
+ }
+
+ private function tokensMatch(Request $request): bool
+ {
+ $expected = (string) $request->getAttribute('session')->token();
+
+ $provided = $request->getParsedBody()['csrfToken'] ??
+ $request->getHeaderLine('X-CSRF-Token');
+
+ return hash_equals($expected, $provided);
+ }
+}
diff --git a/framework/core/src/Http/Middleware/StartSession.php b/framework/core/src/Http/Middleware/StartSession.php
index f5aee1573..4b4678b11 100644
--- a/framework/core/src/Http/Middleware/StartSession.php
+++ b/framework/core/src/Http/Middleware/StartSession.php
@@ -67,7 +67,7 @@ class StartSession implements Middleware
return $this->withSessionCookie($response, $session);
}
- private function makeSession(Request $request)
+ private function makeSession(Request $request): Store
{
return new Store(
$this->config['cookie'],
@@ -76,12 +76,12 @@ class StartSession implements Middleware
);
}
- private function withCsrfTokenHeader(Response $response, Session $session)
+ private function withCsrfTokenHeader(Response $response, Session $session): Response
{
return $response->withHeader('X-CSRF-Token', $session->token());
}
- private function withSessionCookie(Response $response, Session $session)
+ private function withSessionCookie(Response $response, Session $session): Response
{
return FigResponseCookies::set(
$response,
@@ -89,7 +89,7 @@ class StartSession implements Middleware
);
}
- private function getSessionLifetimeInSeconds()
+ private function getSessionLifetimeInSeconds(): int
{
return $this->config['lifetime'] * 60;
}
diff --git a/framework/core/tests/integration/TestCase.php b/framework/core/tests/integration/TestCase.php
index 9d199564e..e705a8967 100644
--- a/framework/core/tests/integration/TestCase.php
+++ b/framework/core/tests/integration/TestCase.php
@@ -11,8 +11,13 @@
namespace Flarum\Tests\integration;
+use Dflydev\FigCookies\SetCookie;
use Flarum\Foundation\InstalledSite;
use Illuminate\Database\ConnectionInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Zend\Diactoros\CallbackStream;
+use Zend\Diactoros\ServerRequest;
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
@@ -24,27 +29,37 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
$this->app();
}
+ /**
+ * @var \Flarum\Foundation\InstalledApp
+ */
protected $app;
+ /**
+ * @var \Psr\Http\Server\RequestHandlerInterface
+ */
+ protected $server;
+
/**
* @return \Flarum\Foundation\InstalledApp
*/
protected function app()
{
- if (! is_null($this->app)) {
- return $this->app;
+ if (is_null($this->app)) {
+ $site = new InstalledSite(
+ [
+ 'base' => __DIR__.'/tmp',
+ 'vendor' => __DIR__.'/../../vendor',
+ 'public' => __DIR__.'/tmp/public',
+ 'storage' => __DIR__.'/tmp/storage',
+ ],
+ include __DIR__.'/tmp/config.php'
+ );
+
+ $this->app = $site->bootApp();
+ $this->server = $this->app->getRequestHandler();
}
- $site = new InstalledSite(
- [
- 'base' => __DIR__.'/tmp',
- 'public' => __DIR__.'/tmp/public',
- 'storage' => __DIR__.'/tmp/storage',
- ],
- include __DIR__.'/tmp/config.php'
- );
-
- return $this->app = $site->bootApp();
+ return $this->app;
}
protected $database;
@@ -67,15 +82,94 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
// First, truncate all referenced tables so that they are empty.
foreach (array_keys($tableData) as $table) {
- $this->database()->table($table)->truncate();
+ if ($table !== 'settings') {
+ $this->database()->table($table)->truncate();
+ }
}
// Then, insert all rows required for this test case.
foreach ($tableData as $table => $rows) {
- $this->database()->table($table)->insert($rows);
+ foreach ($rows as $row) {
+ if ($table === 'settings') {
+ $this->database()->table($table)->updateOrInsert(
+ ['key' => $row['key']],
+ $row
+ );
+ } else {
+ $this->database()->table($table)->updateOrInsert(
+ isset($row['id']) ? ['id' => $row['id']] : $row,
+ $row
+ );
+ }
+ }
}
// And finally, turn on foreign key checks again.
$this->database()->getSchemaBuilder()->enableForeignKeyConstraints();
}
+
+ /**
+ * Send a full HTTP request through Flarum's middleware stack.
+ */
+ protected function send(ServerRequestInterface $request): ResponseInterface
+ {
+ return $this->server->handle($request);
+ }
+
+ /**
+ * Build a HTTP request that can be passed through middleware.
+ *
+ * This method simplifies building HTTP request for use in our HTTP-level
+ * integration tests. It provides options for all features repeatedly being
+ * used in those tests.
+ *
+ * @param string $method
+ * @param string $path
+ * @param array $options
+ * An array of optional request properties.
+ * Currently supported:
+ * - "json" should point to a JSON-serializable object that will be
+ * serialized and used as request body. The corresponding Content-Type
+ * header will be set automatically.
+ * - "cookiesFrom" should hold a response object from a previous HTTP
+ * interaction. All cookies returned from the server in that response
+ * (via the "Set-Cookie" header) will be copied to the cookie params of
+ * the new request.
+ * @return ServerRequestInterface
+ */
+ protected function request(string $method, string $path, array $options = []): ServerRequestInterface
+ {
+ $request = new ServerRequest([], [], $path, $method);
+
+ // Do we want a JSON request body?
+ if (isset($options['json'])) {
+ $request = $request
+ ->withHeader('Content-Type', 'application/json')
+ ->withBody(
+ new CallbackStream(function () use ($options) {
+ return json_encode($options['json']);
+ })
+ );
+ }
+
+ // Let's copy the cookies from a previous response
+ if (isset($options['cookiesFrom'])) {
+ /** @var ResponseInterface $previousResponse */
+ $previousResponse = $options['cookiesFrom'];
+
+ $cookies = array_reduce(
+ $previousResponse->getHeader('Set-Cookie'),
+ function ($memo, $setCookieString) {
+ $setCookie = SetCookie::fromSetCookieString($setCookieString);
+ $memo[$setCookie->getName()] = $setCookie->getValue();
+ return $memo;
+ },
+ []
+ );
+
+ $request = $request->withCookieParams($cookies);
+ }
+
+ return $request;
+ }
}
diff --git a/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php b/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php
index ee9979aa8..c5f8e80b0 100644
--- a/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php
+++ b/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php
@@ -40,6 +40,9 @@ class CreateUserControllerTest extends ApiControllerTestCase
'group_user' => [
['user_id' => 1, 'group_id' => 1],
],
+ 'settings' => [
+ ['key' => 'mail_driver', 'value' => 'log']
+ ]
]);
}
diff --git a/framework/core/tests/integration/api/csrf_protection/RequireCsrfTokenTest.php b/framework/core/tests/integration/api/csrf_protection/RequireCsrfTokenTest.php
new file mode 100644
index 000000000..d52ec519c
--- /dev/null
+++ b/framework/core/tests/integration/api/csrf_protection/RequireCsrfTokenTest.php
@@ -0,0 +1,193 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Flarum\Tests\integration\api\csrf_protection;
+
+use Flarum\Tests\integration\RetrievesAuthorizedUsers;
+use Flarum\Tests\integration\TestCase;
+
+class RequireCsrfTokenTest extends TestCase
+{
+ use RetrievesAuthorizedUsers;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->prepareDatabase([
+ 'users' => [
+ $this->adminUser(),
+ ],
+ 'groups' => [
+ $this->adminGroup(),
+ ],
+ 'group_user' => [
+ ['user_id' => 1, 'group_id' => 1],
+ ],
+ 'group_permission' => [
+ ['permission' => 'viewUserList', 'group_id' => 3],
+ ],
+ 'api_keys' => [
+ ['user_id' => 1, 'key' => 'superadmin'],
+ ],
+ 'settings' => [
+ ['key' => 'csrf_test', 'value' => 1],
+ ],
+ ]);
+ }
+
+ /**
+ * @test
+ */
+ public function error_when_doing_cookie_auth_without_csrf_token()
+ {
+ $auth = $this->send(
+ $this->request(
+ 'POST', '/login',
+ [
+ 'json' => ['identification' => 'admin', 'password' => 'password'],
+ ]
+ )
+ );
+
+ $response = $this->send(
+ $this->request(
+ 'POST', '/api/settings',
+ [
+ 'cookiesFrom' => $auth,
+ 'json' => ['csrf_test' => 2],
+ ]
+ )
+ );
+
+ // Response should be "HTTP 400 Bad Request"
+ $this->assertEquals(400, $response->getStatusCode());
+
+ // The response body should contain proper error details
+ $body = (string) $response->getBody();
+ $this->assertJson($body);
+ $this->assertEquals([
+ 'errors' => [
+ ['status' => '400', 'code' => 'csrf_token_mismatch'],
+ ],
+ ], json_decode($body, true));
+ }
+
+ /**
+ * @test
+ */
+ public function cookie_auth_succeeds_with_csrf_token_in_header()
+ {
+ $initial = $this->send(
+ $this->request('GET', '/')
+ );
+
+ $token = $initial->getHeaderLine('X-CSRF-Token');
+
+ $auth = $this->send(
+ $this->request(
+ 'POST', '/login',
+ [
+ 'cookiesFrom' => $initial,
+ 'json' => ['identification' => 'admin', 'password' => 'password'],
+ ]
+ )->withHeader('X-CSRF-Token', $token)
+ );
+
+ $token = $auth->getHeaderLine('X-CSRF-Token');
+
+ $response = $this->send(
+ $this->request(
+ 'POST', '/api/settings',
+ [
+ 'cookiesFrom' => $auth,
+ 'json' => ['csrf_test' => 2],
+ ]
+ )->withHeader('X-CSRF-Token', $token)
+ );
+
+ // Successful response?
+ $this->assertEquals(204, $response->getStatusCode());
+
+ // Was the setting actually changed in the database?
+ $this->assertEquals(
+ 2,
+ $this->database()->table('settings')->where('key', 'csrf_test')->first()->value
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function cookie_auth_succeeds_with_csrf_token_in_body()
+ {
+ $initial = $this->send(
+ $this->request('GET', '/')
+ );
+
+ $token = $initial->getHeaderLine('X-CSRF-Token');
+
+ $auth = $this->send(
+ $this->request(
+ 'POST', '/login',
+ [
+ 'cookiesFrom' => $initial,
+ 'json' => ['identification' => 'admin', 'password' => 'password', 'csrfToken' => $token],
+ ]
+ )
+ );
+
+ $token = $auth->getHeaderLine('X-CSRF-Token');
+
+ $response = $this->send(
+ $this->request(
+ 'POST', '/api/settings',
+ [
+ 'cookiesFrom' => $auth,
+ 'json' => ['csrf_test' => 2, 'csrfToken' => $token],
+ ]
+ )
+ );
+
+ // Successful response?
+ $this->assertEquals(204, $response->getStatusCode());
+
+ // Was the setting actually changed in the database?
+ $this->assertEquals(
+ 2,
+ $this->database()->table('settings')->where('key', 'csrf_test')->first()->value
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function master_api_token_does_not_need_csrf_token()
+ {
+ $response = $this->send(
+ $this->request(
+ 'POST', '/api/settings',
+ [
+ 'json' => ['csrf_test' => 2],
+ ]
+ )->withHeader('Authorization', 'Token superadmin')
+ );
+
+ // Successful response?
+ $this->assertEquals(204, $response->getStatusCode());
+
+ // Was the setting actually changed in the database?
+ $this->assertEquals(
+ 2,
+ $this->database()->table('settings')->where('key', 'csrf_test')->first()->value
+ );
+ }
+}
diff --git a/framework/core/tests/integration/tmp/storage/.gitkeep b/framework/core/tests/integration/tmp/storage/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/framework/core/views/error/419.blade.php b/framework/core/views/error/419.blade.php
new file mode 100644
index 000000000..a2282323d
--- /dev/null
+++ b/framework/core/views/error/419.blade.php
@@ -0,0 +1,12 @@
+@extends('flarum.forum::layouts.basic')
+
+@section('content')
+
+ {{ $message }}
+
+
+
+ {{ $translator->trans('core.views.error.419_return_link', ['{forum}' => $settings->get('forum_title')]) }}
+
+
+@endsection
From c935f8c74dbc0531b40a806deafaff22005dc8c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?=
Date: Mon, 24 Jun 2019 09:15:15 +0200
Subject: [PATCH 4/5] Apply fixes from StyleCI (#1800)
[ci skip] [skip ci]
---
framework/core/tests/integration/TestCase.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/framework/core/tests/integration/TestCase.php b/framework/core/tests/integration/TestCase.php
index e705a8967..8cd255949 100644
--- a/framework/core/tests/integration/TestCase.php
+++ b/framework/core/tests/integration/TestCase.php
@@ -162,6 +162,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
function ($memo, $setCookieString) {
$setCookie = SetCookie::fromSetCookieString($setCookieString);
$memo[$setCookie->getName()] = $setCookie->getValue();
+
return $memo;
},
[]
From 96bf238aeacbba0e6eb07cf5351d3e3032d9382f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?=
Date: Mon, 24 Jun 2019 10:49:31 +0200
Subject: [PATCH 5/5] removed link to home, go back, which is always the case
with csrf token invalidation
---
framework/core/views/error/419.blade.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/framework/core/views/error/419.blade.php b/framework/core/views/error/419.blade.php
index a2282323d..63c5fb4be 100644
--- a/framework/core/views/error/419.blade.php
+++ b/framework/core/views/error/419.blade.php
@@ -5,8 +5,8 @@
{{ $message }}
-
- {{ $translator->trans('core.views.error.419_return_link', ['{forum}' => $settings->get('forum_title')]) }}
+
+ {{ $translator->trans('core.views.error.419_return_link') }}
@endsection