diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 44540ca..f42c94e 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -12,7 +12,17 @@ class BaseCsrfVerifier implements IMiddleware public const POST_KEY = 'csrf_token'; public const HEADER_KEY = 'X-CSRF-TOKEN'; + /** + * Urls to ignore. You can use * to exclude all sub-urls on a given path. + * For example: /admin/* + * @var array|null + */ protected $except; + /** + * Urls to include. Can be used to include urls from a certain path. + * @var array|null + */ + protected $include; protected $tokenProvider; /** @@ -34,11 +44,7 @@ class BaseCsrfVerifier implements IMiddleware return false; } - $max = count($this->except) - 1; - - for ($i = $max; $i >= 0; $i--) { - $url = $this->except[$i]; - + foreach($this->except as $url) { $url = rtrim($url, '/'); if ($url[strlen($url) - 1] === '*') { $url = rtrim($url, '*'); @@ -48,6 +54,24 @@ class BaseCsrfVerifier implements IMiddleware } if ($skip === true) { + + if($this->include !== null && count($this->include) > 0) { + foreach($this->include as $includeUrl) { + $includeUrl = rtrim($includeUrl, '/'); + if ($includeUrl[strlen($includeUrl) - 1] === '*') { + $includeUrl = rtrim($includeUrl, '*'); + $skip = !$request->getUrl()->contains($includeUrl); + break; + } + + $skip = !($includeUrl === $request->getUrl()->getOriginalUrl()); + } + } + + if($skip === false) { + continue; + } + return true; } } diff --git a/tests/Pecee/SimpleRouter/CsrfVerifierTest.php b/tests/Pecee/SimpleRouter/CsrfVerifierTest.php new file mode 100644 index 0000000..4d05b16 --- /dev/null +++ b/tests/Pecee/SimpleRouter/CsrfVerifierTest.php @@ -0,0 +1,66 @@ +getToken(); + + TestRouter::router()->reset(); + + $router = TestRouter::router(); + $router->getRequest()->setMethod(\Pecee\Http\Request::REQUEST_TYPE_POST); + $router->getRequest()->setUrl(new \Pecee\Http\Url('/page')); + $csrf = new DummyCsrfVerifier(); + $csrf->setTokenProvider($tokenProvider); + + $csrf->handle($router->getRequest()); + + // If handle doesn't throw exception, the test has passed + $this->assertTrue(true); + } + + public function testTokenFail() + { + $this->expectException(\Pecee\Http\Middleware\Exceptions\TokenMismatchException::class); + + global $_POST; + + $tokenProvider = new SilentTokenProvider(); + + $router = TestRouter::router(); + $router->getRequest()->setMethod(\Pecee\Http\Request::REQUEST_TYPE_POST); + $router->getRequest()->setUrl(new \Pecee\Http\Url('/page')); + $csrf = new DummyCsrfVerifier(); + $csrf->setTokenProvider($tokenProvider); + + $csrf->handle($router->getRequest()); + } + + public function testExcludeInclude() + { + $router = TestRouter::router(); + $csrf = new DummyCsrfVerifier(); + $request = $router->getRequest(); + + $request->setUrl(new \Pecee\Http\Url('/exclude-page')); + $this->assertTrue($csrf->testSkip($router->getRequest())); + + $request->setUrl(new \Pecee\Http\Url('/exclude-all/page')); + $this->assertTrue($csrf->testSkip($router->getRequest())); + + $request->setUrl(new \Pecee\Http\Url('/exclude-all/include-page')); + $this->assertFalse($csrf->testSkip($router->getRequest())); + + $request->setUrl(new \Pecee\Http\Url('/include-page')); + $this->assertFalse($csrf->testSkip($router->getRequest())); + } + +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/CsrfVerifier/DummyCsrfVerifier.php b/tests/Pecee/SimpleRouter/Dummy/CsrfVerifier/DummyCsrfVerifier.php new file mode 100644 index 0000000..a695452 --- /dev/null +++ b/tests/Pecee/SimpleRouter/Dummy/CsrfVerifier/DummyCsrfVerifier.php @@ -0,0 +1,18 @@ +skip($request); + } + +} \ No newline at end of file