Browse Source

Allow request whitelisting in CsrfProtectionMiddleware.

ADmad 6 years ago
parent
commit
95af9f3804

+ 29 - 0
src/Http/Middleware/CsrfProtectionMiddleware.php

@@ -67,6 +67,13 @@ class CsrfProtectionMiddleware
     protected $_config = [];
 
     /**
+     * Callback for allowing to skip token check for particular request.
+     *
+     * @var callable|null
+     */
+    protected $whitelistCallback;
+
+    /**
      * Constructor
      *
      * @param array $config Config options. See $_defaultConfig for valid keys.
@@ -86,6 +93,12 @@ class CsrfProtectionMiddleware
      */
     public function __invoke(ServerRequest $request, Response $response, $next)
     {
+        if ($this->whitelistCallback !== null
+            && call_user_func($this->whitelistCallback, $request) === true
+        ) {
+            return $next($request, $response);
+        }
+
         $cookies = $request->getCookieParams();
         $cookieData = Hash::get($cookies, $this->_config['cookieName']);
 
@@ -109,6 +122,22 @@ class CsrfProtectionMiddleware
     }
 
     /**
+     * Set callback for allowing to skip token check for particular request.
+     *
+     * The callback will receive request instance as argument and must return
+     * `true` if you want to skip token check for the particular request.
+     *
+     * @param callable $callback A callable.
+     * @return $this
+     */
+    public function whitelistCallback(callable $callback)
+    {
+        $this->whitelistCallback = $callback;
+
+        return $this;
+    }
+
+    /**
      * Checks if the request is POST, PUT, DELETE or PATCH and validates the CSRF token
      *
      * @param \Cake\Http\ServerRequest $request The request object.

+ 21 - 0
tests/TestCase/Http/Middleware/CsrfProtectionMiddlewareTest.php

@@ -19,6 +19,7 @@ use Cake\Http\Response;
 use Cake\Http\ServerRequest;
 use Cake\I18n\Time;
 use Cake\TestSuite\TestCase;
+use Psr\Http\Message\ServerRequestInterface;
 
 /**
  * Test for CsrfProtection
@@ -309,4 +310,24 @@ class CsrfProtectionMiddlewareTest extends TestCase
         $response = $middleware($request, $response, $this->_getNextClosure());
         $this->assertInstanceOf(Response::class, $response);
     }
+
+    /**
+     * @return void
+     * @doesNotPerformAssertions
+     */
+    public function testFoo()
+    {
+        $request = new ServerRequest([
+            'environment' => [
+                'REQUEST_METHOD' => 'POST',
+                'ALLOW_ME' => 'let-me-pass'
+            ],
+        ]);
+        $response = new Response();
+
+        $middleware = new CsrfProtectionMiddleware();
+        $middleware->whitelistCallback(function (ServerRequestInterface $request): ?bool {
+            return !empty($request->getServerParams()['ALLOW_ME']);
+        });
+    }
 }