-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Transformation limit event listener (#501)
* Add ImageTransformationLimiter Event Listener * Use a parameter array for settings instead of constructor arg * Update tests to use param argument * Update docs with Image transformation limiter section
- Loading branch information
1 parent
449fa94
commit 44efd87
Showing
3 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
/** | ||
* This file is part of the Imbo package | ||
* | ||
* (c) Christer Edvartsen <cogo@starzinger.net> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file that was | ||
* distributed with this source code. | ||
*/ | ||
|
||
namespace Imbo\EventListener; | ||
|
||
use Imbo\EventManager\EventInterface, | ||
Imbo\EventListener\ListenerInterface, | ||
Imbo\Exception\ResourceException; | ||
|
||
/** | ||
* Limit the number of transformations that can be applied in a request. | ||
* | ||
* @author Mats Lindh <mats@lindh.no> | ||
* @package Event\Listeners | ||
*/ | ||
class ImageTransformationLimiter implements ListenerInterface { | ||
/** | ||
* Number of transformations to allow | ||
* | ||
* @var int | ||
*/ | ||
private $transformationLimit; | ||
|
||
/** | ||
* Class constructor | ||
* | ||
* @param array $params Parameters for the limit listener. `limit` (int) is required, and is the max number of | ||
* transformations to allow. 0 will disable the check, but allow the listener to remain | ||
* active. | ||
* @throws InvalidArgumentException Throws an exception if the "limit" element is missing in params | ||
*/ | ||
public function __construct(array $params) { | ||
if (!isset($params['limit'])) { | ||
throw new InvalidArgumentException( | ||
'The image transformation limiter needs the "limit" argument to be configured.', | ||
500 | ||
); | ||
} | ||
|
||
$this->setTransformationLimit($params['limit']); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function getSubscribedEvents() { | ||
return [ | ||
'image.get' => ['checkTransformationCount' => 20], | ||
]; | ||
} | ||
|
||
/** | ||
* Check the number of transformations in a request and generate a 403 if there's an excessive number of | ||
* transformations. | ||
* | ||
* @param EventInterface $event The triggered event | ||
* @throws ResourceException Throws an exception if the transformation count exceeds the allowed value. | ||
*/ | ||
public function checkTransformationCount(EventInterface $event) { | ||
$transformations = $event->getRequest()->getTransformations(); | ||
|
||
if ($this->transformationLimit && (count($transformations) > $this->transformationLimit)) { | ||
throw new ResourceException('Too many transformations applied to resource. The limit is ' . | ||
$this->transformationLimit . ' transformations.', 403); | ||
} | ||
} | ||
|
||
/** | ||
* Get the current transformation limit applied | ||
* | ||
* @return int | ||
*/ | ||
public function getTransformationLimit() { | ||
return $this->transformationLimit; | ||
} | ||
|
||
/** | ||
* Set the current transformation limit. Set value to 0 to disable the check without removing the listener. | ||
* | ||
* @param int $transformationLimit | ||
*/ | ||
public function setTransformationLimit($transformationLimit) { | ||
$this->transformationLimit = (int) $transformationLimit; | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
tests/phpunit/integration/EventListener/ImageTransformationLimiterTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
/** | ||
* This file is part of the Imbo package | ||
* | ||
* (c) Christer Edvartsen <cogo@starzinger.net> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file that was | ||
* distributed with this source code. | ||
*/ | ||
|
||
namespace ImboIntegrationTest\EventListener; | ||
|
||
use Imbo\EventListener\ImageTransformationLimiter; | ||
|
||
/** | ||
* @covers Imbo\EventListener\ExifMetadata | ||
* @group integration | ||
* @group listeners | ||
*/ | ||
class ImageTransformationLimiterTest extends \PHPUnit_Framework_TestCase { | ||
/** | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::__construct | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::checkTransformationCount | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::setTransformationCount | ||
*/ | ||
public function testLimitsTransformationCount() { | ||
$listener = new ImageTransformationLimiter(['limit' => 2]); | ||
|
||
$request = $this->getMock('Imbo\Http\Request\Request'); | ||
|
||
// content of array isn't important, the check is done on the count of the array | ||
$request->expects($this->any())->method('getTransformations')->will($this->returnValue([1, 2, 3, 4, 5])); | ||
|
||
$event = $this->getMock('Imbo\EventManager\Event'); | ||
$event->expects($this->any())->method('getRequest')->will($this->returnValue($request)); | ||
|
||
$this->setExpectedException('Imbo\Exception\ResourceException', '', 403); | ||
$listener->checkTransformationCount($event); | ||
} | ||
|
||
/** | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::__construct | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::checkTransformationCount | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::setTransformationCount | ||
*/ | ||
public function testAllowsTransformationCount() { | ||
$listener = new ImageTransformationLimiter(['limit' => 2]); | ||
|
||
$request = $this->getMock('Imbo\Http\Request\Request'); | ||
|
||
// content of array isn't important, the check is done on the count of the array | ||
$request->expects($this->any())->method('getTransformations')->will($this->returnValue([1, 2])); | ||
|
||
$event = $this->getMock('Imbo\EventManager\Event'); | ||
$event->expects($this->any())->method('getRequest')->will($this->returnValue($request)); | ||
|
||
$listener->checkTransformationCount($event); | ||
} | ||
|
||
/** | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::__construct | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::checkTransformationCount | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::setTransformationCount | ||
*/ | ||
public function testAllowsAnyTransformationCount() { | ||
$listener = new ImageTransformationLimiter(['limit' => 0]); | ||
|
||
$request = $this->getMock('Imbo\Http\Request\Request'); | ||
|
||
// content of array isn't important, the check is done on the count of the array | ||
$request->expects($this->any())->method('getTransformations')->will($this->returnValue([1, 2, 3, 4, 5, 6, 7, 8, 9])); | ||
|
||
$event = $this->getMock('Imbo\EventManager\Event'); | ||
$event->expects($this->any())->method('getRequest')->will($this->returnValue($request)); | ||
|
||
$listener->checkTransformationCount($event); | ||
} | ||
|
||
/** | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::__construct | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::getTransformationCount | ||
* @covers Imbo\EventListener\ImageTransformationLimiter::setTransformationCount | ||
*/ | ||
public function testGetSetLimitCountTransformationCount() { | ||
$listener = new ImageTransformationLimiter(['limit' => 42]); | ||
$this->assertSame(42, $listener->getTransformationLimit()); | ||
|
||
$listener->setTransformationLimit(10); | ||
$this->assertSame(10, $listener->getTransformationLimit()); | ||
} | ||
} |