diff --git a/app/Config/Filters.php b/app/Config/Filters.php index a6c8a2143aac..11359e7c9d01 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -7,9 +7,9 @@ class Filters extends BaseConfig // Makes reading things below nicer, // and simpler to change out script that's used. public $aliases = [ - 'csrf' => \App\Filters\CSRF::class, - 'toolbar' => \App\Filters\DebugToolbar::class, - 'honeypot' => \App\Filters\Honeypot::class, + 'csrf' => \CodeIgniter\Filters\CSRF::class, + 'toolbar' => \CodeIgniter\Filters\DebugToolbar::class, + 'honeypot' => \CodeIgniter\Filters\Honeypot::class, ]; // Always applied before every request diff --git a/app/Filters/Throttle.php b/app/Filters/Throttle.php deleted file mode 100644 index b2659e549eb5..000000000000 --- a/app/Filters/Throttle.php +++ /dev/null @@ -1,46 +0,0 @@ -check($request->getIPAddress(), 60, MINUTE) === false) - { - return Services::response()->setStatusCode(429); - } - } - - //-------------------------------------------------------------------- - - /** - * We don't have anything to do here. - * - * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request - * @param ResponseInterface|\CodeIgniter\HTTP\Response $response - * - * @return mixed - */ - public function after(RequestInterface $request, ResponseInterface $response) - { - } - - //-------------------------------------------------------------------- -} diff --git a/app/Filters/CSRF.php b/system/Filters/CSRF.php similarity index 97% rename from app/Filters/CSRF.php rename to system/Filters/CSRF.php index a5355556ca4a..1319122b04a5 100644 --- a/app/Filters/CSRF.php +++ b/system/Filters/CSRF.php @@ -1,4 +1,4 @@ -locator->listFiles('Filters/'); - $expectedWin = APPPATH . 'Filters\DebugToolbar.php'; - $expectedLin = APPPATH . 'Filters/DebugToolbar.php'; + $expectedWin = SYSTEMPATH . 'Filters\DebugToolbar.php'; + $expectedLin = SYSTEMPATH . 'Filters/DebugToolbar.php'; $this->assertTrue(in_array($expectedWin, $files) || in_array($expectedLin, $files)); $expectedWin = SYSTEMPATH . 'Filters\Filters.php'; diff --git a/tests/system/Filters/CSRFTest.php b/tests/system/Filters/CSRFTest.php new file mode 100644 index 000000000000..697c24534818 --- /dev/null +++ b/tests/system/Filters/CSRFTest.php @@ -0,0 +1,46 @@ +config = new \Config\Filters(); + } + + //-------------------------------------------------------------------- + public function testNormal() + { + $this->config->globals = [ + 'before' => ['csrf'], + 'after' => [], + ]; + + $this->request = Services::request(null, false); + $this->response = Services::response(); + + $filters = new Filters($this->config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + // we expect CSRF requests to be ignored in CLI + $expected = $this->request; + $request = $filters->run($uri, 'before'); + $this->assertEquals($expected, $request); + } + +} diff --git a/tests/system/Filters/DebugToolbarTest.php b/tests/system/Filters/DebugToolbarTest.php new file mode 100644 index 000000000000..5d8f7a774a4b --- /dev/null +++ b/tests/system/Filters/DebugToolbarTest.php @@ -0,0 +1,52 @@ +request = Services::request(); + $this->response = Services::response(); + } + + //-------------------------------------------------------------------- + + public function testDebugToolbarFilter() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + + $config = new FilterConfig(); + $config->globals = [ + 'before' => ['toolbar'], // not normal; exercising its before() + 'after' => ['toolbar'], + ]; + + $filter = new DebugToolbar(); + + $expectedBefore = $this->request; + $expectedAfter = $this->response; + + // nothing should change here, since we have no before logic + $filter->before($this->request); + $this->assertEquals($expectedBefore, $this->request); + + // nothing should change here, since we are running in the CLI + $filter->after($this->request, $this->response); + $this->assertEquals($expectedAfter, $this->response); + } + +} diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index e6f0449cc930..a6bdbf175f88 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -1,6 +1,7 @@ config = new \Config\Filters(); + $this->honey = new \Config\Honeypot(); + + unset($_POST[$this->honey->name]); + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_POST[$this->honey->name] = 'hey'; + } + + //-------------------------------------------------------------------- + public function testBeforeTriggered() + { + $this->config->globals = [ + 'before' => ['honeypot'], + 'after' => [], + ]; + + $this->request = Services::request(null, false); + $this->response = Services::response(); + + $filters = new Filters($this->config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $this->expectException(HoneypotException::class); + $request = $filters->run($uri, 'before'); + } + + //-------------------------------------------------------------------- + public function testBeforeClean() + { + $this->config->globals = [ + 'before' => ['honeypot'], + 'after' => [], + ]; + + unset($_POST[$this->honey->name]); + $this->request = Services::request(null, false); + $this->response = Services::response(); + + $expected = $this->request; + + $filters = new Filters($this->config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $request = $filters->run($uri, 'before'); + $this->assertEquals($expected, $request); + } + + //-------------------------------------------------------------------- + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testAfter() + { + $this->config->globals = [ + 'before' => [], + 'after' => ['honeypot'], + ]; + + $this->request = Services::request(null, false); + $this->response = Services::response(); + + $filters = new Filters($this->config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $this->response->setBody('
'); + $this->response = $filters->run($uri, 'after'); + $this->assertContains($this->honey->name, $this->response->getBody()); + } + + //-------------------------------------------------------------------- + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testAfterNotApplicable() + { + $this->config->globals = [ + 'before' => [], + 'after' => ['honeypot'], + ]; + + $this->request = Services::request(null, false); + $this->response = Services::response(); + + $filters = new Filters($this->config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $this->response->setBody('
'); + $this->response = $filters->run($uri, 'after'); + $this->assertNotContains($this->honey->name, $this->response->getBody()); + } + +} diff --git a/tests/system/Honeypot/HoneypotTest.php b/tests/system/Honeypot/HoneypotTest.php index b7346f45b8ee..38bb8ebe7cbc 100644 --- a/tests/system/Honeypot/HoneypotTest.php +++ b/tests/system/Honeypot/HoneypotTest.php @@ -7,8 +7,6 @@ use CodeIgniter\Honeypot\Exceptions\HoneypotException; use CodeIgniter\Test\CIUnitTestCase; -require_once __DIR__ . '/fixtures/HoneyTrap.php'; - /** * @backupGlobals enabled */ @@ -90,7 +88,7 @@ public function testConfigName() public function testHoneypotFilterBefore() { $config = [ - 'aliases' => ['trap' => 'CodeIgniter\Honeypot\fixtures\HoneyTrap'], + 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => ['trap'], 'after' => [], @@ -107,7 +105,7 @@ public function testHoneypotFilterBefore() public function testHoneypotFilterAfter() { $config = [ - 'aliases' => ['trap' => 'CodeIgniter\Honeypot\fixtures\HoneyTrap'], + 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => [], 'after' => ['trap'], diff --git a/tests/system/Honeypot/fixtures/HoneyTrap.php b/tests/system/Honeypot/fixtures/HoneyTrap.php deleted file mode 100644 index 8cc3aceef95e..000000000000 --- a/tests/system/Honeypot/fixtures/HoneyTrap.php +++ /dev/null @@ -1,43 +0,0 @@ -hasContent($request)) - { - throw HoneypotException::isBot(); - } - } - - /** - * Attach a honypot to the current response. - * - * @param CodeIgniter\HTTP\RequestInterface $request - * @param CodeIgniter\HTTP\ResponseInterface $response - * @return mixed - */ - public function after(RequestInterface $request, ResponseInterface $response) - { - $honeypot = new Honeypot(new \Config\Honeypot()); - $honeypot->attachHoneypot($response); - } - -} diff --git a/user_guide_src/source/incoming/filters.rst b/user_guide_src/source/incoming/filters.rst index e18687ddb3dc..39538c03851d 100644 --- a/user_guide_src/source/incoming/filters.rst +++ b/user_guide_src/source/incoming/filters.rst @@ -80,9 +80,9 @@ and you cannot stop script execution. This does allow you to modify the final ou the final output. This could be used to ensure certain security headers were set the correct way, or to cache the final output, or even to filter the final output with a bad words filter. -=================== +******************* Configuring Filters -=================== +******************* Once you've created your filters, you need to configure when they get run. This is done in ``app/Config/Filters.php``. This file contains four properties that allow you to configure exactly when the filters run. @@ -94,7 +94,7 @@ The ``$aliases`` array is used to associate a simple name with one or more fully filters to run:: public $aliases = [ - 'csrf' => \App\Filters\CSRF::class + 'csrf' => \CodeIgniter\Filters\CSRF::class ]; Aliases are mandatory and if you try to use a full class name later, the system will throw an error. Defining them @@ -181,4 +181,4 @@ a list of URI patterns that filter should apply to:: Provided Filters **************** -Three filters are bundled with CodeIgniter4: Honeypot, Security, and Throttler. +Three filters are bundled with CodeIgniter4: Honeypot, Security, and DebugToolbar. diff --git a/user_guide_src/source/incoming/incomingrequest.rst b/user_guide_src/source/incoming/incomingrequest.rst index b608ed6f7e6b..6534f5240538 100644 --- a/user_guide_src/source/incoming/incomingrequest.rst +++ b/user_guide_src/source/incoming/incomingrequest.rst @@ -270,8 +270,9 @@ You can easily negotiate content types with the request through the ``negotiate( See the :doc:`Content Negotiation ` page for more details. +*************** Class Reference ---------------- +*************** .. note:: In addition to the methods listed here, this class inherits the methods from the :doc:`Request Class ` and the :doc:`Message Class `. diff --git a/user_guide_src/source/incoming/request.rst b/user_guide_src/source/incoming/request.rst index 20c0c261fdbf..03009539658a 100644 --- a/user_guide_src/source/incoming/request.rst +++ b/user_guide_src/source/incoming/request.rst @@ -11,9 +11,9 @@ from the Request class to add specific functionality. See the documentation for the :doc:`IncomingRequest Class ` and :doc:`CURLRequest Class ` for more usage details. -=============== +*************** Class Reference -=============== +*************** .. php:class:: CodeIgniter\\HTTP\\Request diff --git a/user_guide_src/source/libraries/caching.rst b/user_guide_src/source/libraries/caching.rst index 7e5ab7c09e45..28765401ce47 100644 --- a/user_guide_src/source/libraries/caching.rst +++ b/user_guide_src/source/libraries/caching.rst @@ -71,9 +71,9 @@ This is an array of servers that will be used when using the ``Memcache(d)`` han The settings for the Redis server that you wish to use when using the ``Redis`` handler. -=============== +*************** Class Reference -=============== +*************** .. php:method:: isSupported() diff --git a/user_guide_src/source/libraries/honeypot.rst b/user_guide_src/source/libraries/honeypot.rst index 0e40bde77702..dc85f7a1bb44 100644 --- a/user_guide_src/source/libraries/honeypot.rst +++ b/user_guide_src/source/libraries/honeypot.rst @@ -28,7 +28,9 @@ from the ``$globals`` array, like...:: ] ]; -A sample Honeypot filter is bundled, as ``app/Filters/Honeypot.php``. +A sample Honeypot filter is bundled, as ``system/Filters/Honeypot.php``. +If it is not suitable, make your own at ``app/Filters/Honeypot.php``, +and modify the ``$aliases`` in the configuration appropriately. Customizing Honeypot ===================== diff --git a/user_guide_src/source/libraries/security.rst b/user_guide_src/source/libraries/security.rst index d7dab3680fb7..05ed86683f34 100644 --- a/user_guide_src/source/libraries/security.rst +++ b/user_guide_src/source/libraries/security.rst @@ -28,6 +28,7 @@ and enabling the `csrf` filter globally:: public $globals = [ 'before' => [ + //'honeypot' 'csrf' ] ]; diff --git a/user_guide_src/source/libraries/throttler.rst b/user_guide_src/source/libraries/throttler.rst index 92f24492c816..a1341b79b91f 100644 --- a/user_guide_src/source/libraries/throttler.rst +++ b/user_guide_src/source/libraries/throttler.rst @@ -49,20 +49,53 @@ start using it in your application. The Code ======== -You can find this file at **app/Filters/Throttle.php** but the relevant method is reproduced here:: - - public function before(RequestInterface $request) - { - $throttler = Services::throttler(); - - // Restrict an IP address to no more - // than 1 request per second across the - // entire site. - if ($throttler->check($request->getIPAddress(), 60, MINUTE) === false) - { - return Services::response()->setStatusCode(429); - } - } +You could make your own Throttler filter, at **app/Filters/Throttle.php**, +along the lines of:: + + check($request->getIPAddress(), 60, MINUTE) === false) + { + return Services::response()->setStatusCode(429); + } + } + + //-------------------------------------------------------------------- + + /** + * We don't have anything to do here. + * + * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request + * @param ResponseInterface|\CodeIgniter\HTTP\Response $response + * + * @return mixed + */ + public function after(RequestInterface $request, ResponseInterface $response) + { + } + } When run, this method first grabs an instance of the throttler. Next it uses the IP address as the bucket name, and sets things to limit them to one request per second. If the throttler rejects the check, returning false, @@ -79,8 +112,7 @@ this to incoming requests, you need to edit **/app/Config/Filters.php** and firs filter:: public $aliases = [ - 'csrf' => \App\Filters\CSRF::class, - 'toolbar' => \App\Filters\DebugToolbar::class, + ... 'throttle' => \App\Filters\Throttle::class ]; @@ -92,9 +124,9 @@ Next, we assign it to all POST requests made on the site:: And that's all there is to it. Now all POST requests made on the site will have be rate limited. -=============== +*************** Class Reference -=============== +*************** .. php:method:: check(string $key, int $capacity, int $seconds[, int $cost = 1]) diff --git a/user_guide_src/source/outgoing/view_renderer.rst b/user_guide_src/source/outgoing/view_renderer.rst index f79d9dda2ab8..735e51a5d962 100644 --- a/user_guide_src/source/outgoing/view_renderer.rst +++ b/user_guide_src/source/outgoing/view_renderer.rst @@ -99,8 +99,9 @@ Several options can be passed to the ``render()`` or ``renderString()`` methods: ignored for renderString() - ``saveData`` - true if the view data parameters should be retained for subsequent calls +*************** Class Reference -=============== +*************** .. php:class:: CodeIgniter\\View\\View