Skip to content

Commit

Permalink
Merge pull request #405 from trsteel88/master
Browse files Browse the repository at this point in the history
Add a Signer Utility to sign filters, run php-cs-fixer on bundle
  • Loading branch information
makasim committed May 22, 2014
2 parents e3a28ed + f14702e commit b312ed3
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 105 deletions.
89 changes: 63 additions & 26 deletions Controller/ImagineController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@

namespace Liip\ImagineBundle\Controller;

use Imagine\Image\ImagineInterface;
use Imagine\Exception\RuntimeException;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Data\DataManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;

use Liip\ImagineBundle\Imagine\Cache\SignerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\UriSigner;

class ImagineController
{
Expand All @@ -32,26 +31,26 @@ class ImagineController
protected $cacheManager;

/**
* @var UriSigner
* @var SignerInterface
*/
protected $uriSigner;
protected $signer;

/**
* @param DataManager $dataManager
* @param FilterManager $filterManager
* @param CacheManager $cacheManager
* @param UriSigner $uriSigner
* @param DataManager $dataManager
* @param FilterManager $filterManager
* @param CacheManager $cacheManager
* @param SignerInterface $signer
*/
public function __construct(
DataManager $dataManager,
FilterManager $filterManager,
CacheManager $cacheManager,
UriSigner $uriSigner
SignerInterface $signer
) {
$this->dataManager = $dataManager;
$this->filterManager = $filterManager;
$this->cacheManager = $cacheManager;
$this->uriSigner = $uriSigner;
$this->signer = $signer;
}

/**
Expand All @@ -69,17 +68,6 @@ public function __construct(
public function filterAction(Request $request, $path, $filter)
{
try {
$runtimeConfig = array();
$pathPrefix = '';
if ($runtimeFilters = $request->query->get('filters', array())) {
if (false == $this->uriSigner->check($request->getSchemeAndHttpHost().$request->getRequestUri())) {
throw new BadRequestHttpException('Signed url does not pass the sign check. Maybe it was modified by someone.');
}

$runtimeConfig['filters'] = $runtimeFilters;
$pathPrefix = substr($request->query->get('_hash'), 0, 8).'/';
}

if (!$this->cacheManager->isStored($path, $filter)) {
try {
$binary = $this->dataManager->find($filter, $path);
Expand All @@ -89,15 +77,64 @@ public function filterAction(Request $request, $path, $filter)
}

$this->cacheManager->store(
$this->filterManager->applyFilter($binary, $filter, $runtimeConfig),
$pathPrefix.$path,
$this->filterManager->applyFilter($binary, $filter),
$path,
$filter
);
}

return new RedirectResponse($this->cacheManager->resolve($pathPrefix.$path, $filter), 301);
return new RedirectResponse($this->cacheManager->resolve($path, $filter), 301);
} catch (RuntimeException $e) {
throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $path, $filter, $e->getMessage()), 0, $e);
}
}

/**
* This action applies a given filter to a given image, optionally saves the image and outputs it to the browser at the same time.
*
* @param Request $request
* @param string $hash
* @param string $path
* @param string $filter
*
* @throws \RuntimeException
* @throws BadRequestHttpException
*
* @return RedirectResponse
*/
public function filterRuntimeAction(Request $request, $hash, $path, $filter)
{
try {
$filters = $request->query->get('filters', array());

if (true !== $this->signer->check($hash, $path, $filters)) {
throw new BadRequestHttpException(sprintf(
'Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s',
$path,
$filter,
json_encode($filters)
));
}

try {
$binary = $this->dataManager->find($filter, $path);
} catch (NotLoadableException $e) {
throw new NotFoundHttpException(sprintf('Source image could not be found for path "%s" and filter "%s"', $path, $filter), $e);
}

$cachePrefix = 'rc/'.$hash;

$this->cacheManager->store(
$this->filterManager->applyFilter($binary, $filter, array(
'filters' => $filters,
)),
$cachePrefix.'/'.$path,
$filter
);

return new RedirectResponse($this->cacheManager->resolve($cachePrefix.'/'.$path, $filter), 301);
} catch (RuntimeException $e) {
throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $pathPrefix.$path, $filter, $e->getMessage()), 0, $e);
throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $hash.'/'.$path, $filter, $e->getMessage()), 0, $e);
}
}
}
9 changes: 7 additions & 2 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ public function getConfigTreeBuilder()
->scalarNode('cache')->defaultValue('default')->end()
->scalarNode('cache_base_path')->defaultValue('')->end()
->scalarNode('data_loader')->defaultValue('default')->end()
->scalarNode('controller_action')->defaultValue('liip_imagine.controller:filterAction')->end()
->arrayNode('controller')
->addDefaultsIfNotSet()
->children()
->scalarNode('filter_action')->defaultValue('liip_imagine.controller:filterAction')->end()
->scalarNode('filter_runtime_action')->defaultValue('liip_imagine.controller:filterRuntimeAction')->end()
->end()
->end()
->arrayNode('filter_sets')
->useAttributeAsKey('name')
->prototype('array')
Expand All @@ -115,7 +121,6 @@ public function getConfigTreeBuilder()
->scalarNode('quality')->defaultValue(100)->end()
->scalarNode('cache')->defaultNull()->end()
->scalarNode('data_loader')->defaultNull()->end()
->scalarNode('controller_action')->defaultNull()->end()
->arrayNode('filters')
->useAttributeAsKey('name')
->prototype('array')
Expand Down
3 changes: 2 additions & 1 deletion DependencyInjection/LiipImagineExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public function load(array $configs, ContainerBuilder $container)

$container->setParameter('liip_imagine.binary.loader.default', $config['data_loader']);

$container->setParameter('liip_imagine.controller_action', $config['controller_action']);
$container->setParameter('liip_imagine.controller.filter_action', $config['controller']['filter_action']);
$container->setParameter('liip_imagine.controller.filter_runtime_action', $config['controller']['filter_runtime_action']);

$resources = $container->hasParameter('twig.form.resources') ? $container->getParameter('twig.form.resources') : array();
$resources[] = 'LiipImagineBundle:Form:form_div_layout.html.twig';
Expand Down
23 changes: 11 additions & 12 deletions Imagine/Cache/CacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Liip\ImagineBundle\Binary\BinaryInterface;
use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface;
use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration;
use Liip\ImagineBundle\Imagine\Cache\SignerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\UriSigner;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\Event;
Expand All @@ -31,9 +31,9 @@ class CacheManager
protected $resolvers = array();

/**
* @var UriSigner
* @var SignerInterface
*/
protected $uriSigner;
protected $signer;

/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
Expand All @@ -50,19 +50,19 @@ class CacheManager
*
* @param FilterConfiguration $filterConfig
* @param RouterInterface $router
* @param UriSigner $uriSigner
* @param SignerInterface $signer
* @param string $defaultResolver
*/
public function __construct(
FilterConfiguration $filterConfig,
RouterInterface $router,
UriSigner $uriSigner,
SignerInterface $signer,
EventDispatcherInterface $dispatcher,
$defaultResolver = null
) {
$this->filterConfig = $filterConfig;
$this->router = $router;
$this->uriSigner = $uriSigner;
$this->signer = $signer;
$this->dispatcher = $dispatcher;
$this->defaultResolver = $defaultResolver ?: 'default';
}
Expand Down Expand Up @@ -150,14 +150,13 @@ public function generateUrl($path, $filter, array $runtimeConfig = array())
'filter' => $filter
);

if (!empty($runtimeConfig)) {
if (empty($runtimeConfig)) {
$filterUrl = $this->router->generate('liip_imagine_filter', $params, true);
} else {
$params['filters'] = $runtimeConfig;
}
$params['hash'] = $this->signer->sign($path, $runtimeConfig);

$filterUrl = $this->router->generate('liip_imagine_filter', $params, true);

if (!empty($runtimeConfig)) {
$filterUrl = $this->uriSigner->sign($filterUrl);
$filterUrl = $this->router->generate('liip_imagine_filter_runtime', $params, true);
}

return $filterUrl;
Expand Down
41 changes: 41 additions & 0 deletions Imagine/Cache/Signer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Liip\ImagineBundle\Imagine\Cache;

class Signer implements SignerInterface
{
/**
* @var string
*/
private $secret;

/**
* @param string $secret
*/
public function __construct($secret)
{
$this->secret = $secret;
}

/**
* {@inheritdoc}
*/
public function sign($path, array $runtimeConfig = null)
{
if ($runtimeConfig) {
array_walk_recursive($runtimeConfig, function (&$value) {
$value = (string)$value;
});
}

return substr(preg_replace('/[^a-zA-Z0-9-_]/', '', base64_encode(hash_hmac('sha256', ltrim($path, '/') . (null === $runtimeConfig ?: serialize($runtimeConfig)), $this->secret, true))), 0, 8);
}

/**
* {@inheritdoc}
*/
public function check($hash, $path, array $runtimeConfig = null)
{
return $hash === $this->sign($path, $runtimeConfig);
}
}
25 changes: 25 additions & 0 deletions Imagine/Cache/SignerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Liip\ImagineBundle\Imagine\Cache;

interface SignerInterface
{
/**
* Return the hash for path and runtime config
*
* @param string $path
* @param array $runtimeConfig
* @return string
*/
public function sign($path, array $runtimeConfig = null);

/**
* Check hash is correct
*
* @param string $hash
* @param string $path
* @param array $runtimeConfig
* @return bool
*/
public function check($hash, $path, array $runtimeConfig = null);
}
9 changes: 6 additions & 3 deletions Resources/config/imagine.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<parameter key="liip_imagine.filter.manager.class">Liip\ImagineBundle\Imagine\Filter\FilterManager</parameter>
<parameter key="liip_imagine.data.manager.class">Liip\ImagineBundle\Imagine\Data\DataManager</parameter>
<parameter key="liip_imagine.cache.manager.class">Liip\ImagineBundle\Imagine\Cache\CacheManager</parameter>
<parameter key="liip_imagine.cache.signer.class">Liip\ImagineBundle\Imagine\Cache\Signer</parameter>
<parameter key="liip_imagine.binary.mime_type_guesser.class">Liip\ImagineBundle\Binary\SimpleMimeTypeGuesser</parameter>

<!-- Controller class -->
Expand Down Expand Up @@ -79,7 +80,7 @@
<service id="liip_imagine.cache.manager" class="%liip_imagine.cache.manager.class%">
<argument type="service" id="liip_imagine.filter.configuration" />
<argument type="service" id="router" />
<argument type="service" id="liip_imagine.uri_signer" />
<argument type="service" id="liip_imagine.cache.signer" />
<argument type="service" id="event_dispatcher" />
<argument>%liip_imagine.cache.resolver.default%</argument>
</service>
Expand All @@ -94,7 +95,7 @@
<argument type="service" id="liip_imagine.data.manager" />
<argument type="service" id="liip_imagine.filter.manager" />
<argument type="service" id="liip_imagine.cache.manager" />
<argument type="service" id="liip_imagine.uri_signer" />
<argument type="service" id="liip_imagine.cache.signer" />
</service>

<!-- ImagineInterface instances -->
Expand Down Expand Up @@ -236,6 +237,8 @@
<argument type="service" id="liip_imagine.mime_type_guesser" />
</service>

<service id="liip_imagine.uri_signer" alias="uri_signer" />
<service id="liip_imagine.cache.signer" class="%liip_imagine.cache.signer.class%">
<argument>%kernel.secret%</argument>
</service>
</services>
</container>
8 changes: 7 additions & 1 deletion Resources/config/routing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">

<route id="liip_imagine_filter_runtime" path="/media/cache/{filter}/rc/{hash}/{path}" methods="GET">
<default key="_controller">%liip_imagine.controller.filter_runtime_action%</default>
<requirement key="filter">[A-z0-9_\-]*</requirement>
<requirement key="path">.+</requirement>
</route>

<route id="liip_imagine_filter" path="/media/cache/{filter}/{path}" methods="GET">
<default key="_controller">%liip_imagine.controller_action%</default>
<default key="_controller">%liip_imagine.controller.filter_action%</default>
<requirement key="filter">[A-z0-9_\-]*</requirement>
<requirement key="path">.+</requirement>
</route>
Expand Down
Loading

0 comments on commit b312ed3

Please sign in to comment.