Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Loader] Abstract filesystem resource locator and legacy insecure locator implementation #866

Merged
merged 1 commit into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ install:
- composer update $COMPOSER_FLAGS

script:
- ./bin/phpunit -vvv
- SYMFONY_DEPRECATIONS_HELPER=weak ./bin/phpunit -vvv

after_script:
- if [ "${TRAVIS_PHP_VERSION}" != "5.3" ]; then bin/coveralls -vvv; fi;
Expand Down
70 changes: 20 additions & 50 deletions Binary/Loader/FileSystemLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

namespace Liip\ImagineBundle\Binary\Loader;

use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;
use Liip\ImagineBundle\Binary\Locator\FileSystemLocator;
use Liip\ImagineBundle\Binary\Locator\LocatorInterface;
use Liip\ImagineBundle\Exception\InvalidArgumentException;
use Liip\ImagineBundle\Model\FileBinary;
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesserInterface;
Expand All @@ -30,78 +31,47 @@ class FileSystemLoader implements LoaderInterface
protected $extensionGuesser;

/**
* @var string[]
* @var LocatorInterface
*/
protected $dataRoots;
protected $locator;

/**
* @param MimeTypeGuesserInterface $mimeTypeGuesser
* @param MimeTypeGuesserInterface $mimeGuesser
* @param ExtensionGuesserInterface $extensionGuesser
* @param string[] $dataRoots
* @param LocatorInterface $locator
*/
public function __construct(
MimeTypeGuesserInterface $mimeTypeGuesser,
MimeTypeGuesserInterface $mimeGuesser,
ExtensionGuesserInterface $extensionGuesser,
$dataRoots
/* LocatorInterface $locator */
) {
$this->mimeTypeGuesser = $mimeTypeGuesser;
$this->mimeTypeGuesser = $mimeGuesser;
$this->extensionGuesser = $extensionGuesser;

$this->dataRoots = array_map(function ($root) {
if (!empty($root) && false !== $realRoot = realpath($root)) {
return $realRoot;
}

throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $root));
}, (array) $dataRoots);

if (count($this->dataRoots) === 0) {
if (count($dataRoots) === 0) {
throw new InvalidArgumentException('One or more data root paths must be specified.');
}

if (func_num_args() >= 4 && false === ($this->locator = func_get_arg(3)) instanceof LocatorInterface) {
throw new \InvalidArgumentException(sprintf('Method %s() expects a LocatorInterface for the forth argument.', __METHOD__));
} elseif (func_num_args() < 4) {
@trigger_error(sprintf('Method %s() will have a forth `LocatorInterface $locator` argument in version 2.0. Not defining it is deprecated since version 1.7.2', __METHOD__), E_USER_DEPRECATED);
$this->locator = new FileSystemLocator();
}

$this->locator->setOptions(array('roots' => (array) $dataRoots));
}

/**
* {@inheritdoc}
*/
public function find($path)
{
$path = $this->absolutePathRestrict($this->absolutePathLocate($path));
$path = $this->locator->locate($path);
$mime = $this->mimeTypeGuesser->guess($path);

return new FileBinary($path, $mime, $this->extensionGuesser->guess($mime));
}

/**
* @param string $path
*
* @return string
*/
private function absolutePathLocate($path)
{
foreach ($this->dataRoots as $root) {
if (false !== $realPath = realpath($root.DIRECTORY_SEPARATOR.$path)) {
return $realPath;
}
}

throw new NotLoadableException(sprintf('Source image not resolvable "%s" in root path(s) "%s"',
$path, implode(':', $this->dataRoots)));
}

/**
* @param string $path
*
* @return mixed
*/
private function absolutePathRestrict($path)
{
foreach ($this->dataRoots as $root) {
if (0 === strpos($path, $root)) {
return $path;
}
}

throw new NotLoadableException(sprintf('Source image invalid "%s" as it is outside of the defined root path(s) "%s"',
$path, implode(':', $this->dataRoots)));
}
}
30 changes: 30 additions & 0 deletions Binary/Locator/FileSystemInsecureLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the `liip/LiipImagineBundle` project.
*
* (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace Liip\ImagineBundle\Binary\Locator;

class FileSystemInsecureLocator extends FileSystemLocator
{
/**
* @param string $root
* @param string $path
*
* @return string|false
*/
protected function generateAbsolutePath($root, $path)
{
if (false !== strpos($path, '..'.DIRECTORY_SEPARATOR)) {
return false;
}

return file_exists($absolute = $root.DIRECTORY_SEPARATOR.$path) ? $absolute : false;
}
}
101 changes: 101 additions & 0 deletions Binary/Locator/FileSystemLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

/*
* This file is part of the `liip/LiipImagineBundle` project.
*
* (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace Liip\ImagineBundle\Binary\Locator;

use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;
use Liip\ImagineBundle\Exception\InvalidArgumentException;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FileSystemLocator implements LocatorInterface
{
/**
* @var string[]
*/
protected $roots;

/**
* @param array[] $options
*/
public function setOptions(array $options = array())
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setDefaults(array('roots' => array()));
$options = $optionsResolver->resolve($options);

$this->roots = array_map(array($this, 'sanitizeRootPath'), (array) $options['roots']);
}

/**
* @param string $path
*
* @throws NotLoadableException
*
* @return string
*/
public function locate($path)
{
foreach ($this->roots as $root) {
if (false !== $absolute = $this->generateAbsolutePath($root, $path)) {
return $this->sanitizeAbsolutePath($absolute);
}
}

throw new NotLoadableException(sprintf('Source image not resolvable "%s" in root path(s) "%s"',
$path, implode(':', $this->roots)));
}

/**
* @param string $root
* @param string $path
*
* @return string|false
*/
protected function generateAbsolutePath($root, $path)
{
return realpath($root.DIRECTORY_SEPARATOR.$path);
}

/**
* @param string $root
*
* @throws InvalidArgumentException
*
* @return string
*/
protected function sanitizeRootPath($root)
{
if (!empty($root) && false !== $realRoot = realpath($root)) {
return $realRoot;
}

throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $root));
}

/**
* @param string $path
*
* @throws NotLoadableException
*
* @return string
*/
private function sanitizeAbsolutePath($path)
{
foreach ($this->roots as $root) {
if (0 === strpos($path, $root)) {
return $path;
}
}

throw new NotLoadableException(sprintf('Source image invalid "%s" as it is outside of the defined root path(s) "%s"',
$path, implode(':', $this->roots)));
}
}
27 changes: 27 additions & 0 deletions Binary/Locator/LocatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the `liip/LiipImagineBundle` project.
*
* (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace Liip\ImagineBundle\Binary\Locator;

interface LocatorInterface
{
/**
* @param array $options[]
*/
public function setOptions(array $options = array());

/**
* @param string $path
*
* @return string
*/
public function locate($path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class FileSystemLoaderFactory extends AbstractLoaderFactory
{
Expand All @@ -23,6 +24,7 @@ public function create(ContainerBuilder $container, $loaderName, array $config)
{
$definition = $this->getChildLoaderDefinition();
$definition->replaceArgument(2, $config['data_root']);
$definition->replaceArgument(3, new Reference(sprintf('liip_imagine.binary.locator.%s', $config['locator'])));

return $this->setTaggedLoaderDefinition($loaderName, $definition, $container);
}
Expand All @@ -42,6 +44,11 @@ public function addConfiguration(ArrayNodeDefinition $builder)
{
$builder
->children()
->enumNode('locator')
->values(array('filesystem', 'filesystem_insecure'))
->info('Using the "filesystem_insecure" locator is not recommended due to a less secure resolver mechanism, but is provided for those using heavily symlinked projects.')
->defaultValue('filesystem')
->end()
->arrayNode('data_root')
->beforeNormalization()
->ifString()
Expand Down
14 changes: 14 additions & 0 deletions Resources/config/imagine.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
<parameter key="liip_imagine.binary.loader.stream.class">Liip\ImagineBundle\Binary\Loader\StreamLoader</parameter>
<parameter key="liip_imagine.binary.loader.flysystem.class">Liip\ImagineBundle\Binary\Loader\FlysystemLoader</parameter>

<!-- Data loader loaders' classes -->

<parameter key="liip_imagine.binary.locator.filesystem.class">Liip\ImagineBundle\Binary\Locator\FileSystemLocator</parameter>
<parameter key="liip_imagine.binary.locator.filesystem_insecure.class">Liip\ImagineBundle\Binary\Locator\FileSystemInsecureLocator</parameter>

<!-- Cache resolvers' classes -->

<parameter key="liip_imagine.cache.resolver.web_path.class">Liip\ImagineBundle\Imagine\Cache\Resolver\WebPathResolver</parameter>
Expand Down Expand Up @@ -226,6 +231,7 @@
<argument type="service" id="liip_imagine.mime_type_guesser" />
<argument type="service" id="liip_imagine.extension_guesser" />
<argument><!-- will be injected by FileSystemLoaderFactory --></argument>
<argument><!-- will be injected by FileSystemLoaderFactory --></argument>
</service>

<service id="liip_imagine.binary.loader.prototype.stream" class="%liip_imagine.binary.loader.stream.class%">
Expand All @@ -238,6 +244,14 @@
<argument><!-- will be injected by FlysystemLoaderFactory --></argument>
</service>

<!-- Data loader locators -->

<service id="liip_imagine.binary.locator.filesystem" class="%liip_imagine.binary.locator.filesystem.class%" public="false">
</service>

<service id="liip_imagine.binary.locator.filesystem_insecure" class="%liip_imagine.binary.locator.filesystem_insecure.class%" public="false">
</service>

<!-- Cache resolver -->

<service id="liip_imagine.cache.resolver.prototype.web_path" class="%liip_imagine.cache.resolver.web_path.class%" public="true" abstract="true">
Expand Down
Loading