Skip to content

Commit

Permalink
Merge pull request #21 from punktDeForks/task/flow-70-compatibility
Browse files Browse the repository at this point in the history
FEATURE: Flow 7+ compatibility
  • Loading branch information
bwaidelich authored Apr 19, 2021
2 parents ca494ee + b59bedc commit e9a3474
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
* */

use Neos\Flow\Http\Response as HttpResponse;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;

Expand Down
9 changes: 4 additions & 5 deletions Classes/Http/FileServeStrategy/ReadfileStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
* */

use GuzzleHttp\Psr7\Utils;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use Wwwision\PrivateResources\Http\Middleware\Exception\FileNotFoundException;
use Wwwision\PrivateResources\Utility\ProtectedResourceUtility;

use function GuzzleHttp\Psr7\stream_for;

/**
* A file serve strategy that outputs the given file using PHPs readfile function
*
Expand All @@ -25,13 +25,12 @@ class ReadfileStrategy implements FileServeStrategyInterface
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @param array $options
* @return HttpResponseInterface
* @throws FileNotFoundException
*/
public function serve(PersistentResource $resource, HttpResponseInterface $httpResponse, array $options): HttpResponseInterface
{
$filePathAndName = ProtectedResourceUtility::getStoragePathAndFilenameByHash($resource->getSha1(), $options['basePath']);

/** @var HttpResponseInterface $response */
$response = $httpResponse->withBody(stream_for(fopen($filePathAndName, 'rb')));
return $response;
return $httpResponse->withBody(Utils::streamFor(fopen($filePathAndName, 'rb')));
}
}
8 changes: 2 additions & 6 deletions Classes/Http/FileServeStrategy/StreamStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
* */

use GuzzleHttp\Psr7\Utils;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use Wwwision\PrivateResources\Utility\ProtectedResourceUtility;

use function GuzzleHttp\Psr7\stream_for;

/**
* A file serve strategy that streams the given resource
Expand All @@ -30,9 +28,7 @@ public function serve(PersistentResource $resource, HttpResponseInterface $httpR
{
$stream = $resource->getStream();

/** @var HttpResponseInterface $response */
$response = $httpResponse->withBody(stream_for($stream));
return $response;
return $httpResponse->withBody(Utils::streamFor($stream));
}

}
7 changes: 3 additions & 4 deletions Classes/Http/FileServeStrategy/XAccelRedirectStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use Wwwision\PrivateResources\Http\Middleware\Exception\FileNotFoundException;
use Wwwision\PrivateResources\Utility\ProtectedResourceUtility;

/**
Expand All @@ -26,13 +27,11 @@ class XAccelRedirectStrategy implements FileServeStrategyInterface
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @param array $options
* @return HttpResponseInterface
* @throws FileNotFoundException
*/
public function serve(PersistentResource $resource, HttpResponseInterface $httpResponse, array $options): HttpResponseInterface
{
$filePathAndName = ProtectedResourceUtility::getStoragePathAndFilenameByHash($resource->getSha1(), $options['basePath']);

/** @var HttpResponseInterface $response */
$response = $httpResponse->withHeader('X-Accel-Redirect', $filePathAndName);
return $response;
return $httpResponse->withHeader('X-Accel-Redirect', $filePathAndName);
}
}
8 changes: 3 additions & 5 deletions Classes/Http/FileServeStrategy/XSendfileStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use Wwwision\PrivateResources\Http\Middleware\Exception\FileNotFoundException;
use Wwwision\PrivateResources\Utility\ProtectedResourceUtility;

/**
Expand All @@ -25,14 +26,11 @@ class XSendfileStrategy implements FileServeStrategyInterface
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @param array $options
* @return HttpResponseInterface
* @throws FileNotFoundException
*/
public function serve(PersistentResource $resource, HttpResponseInterface $httpResponse, array $options): HttpResponseInterface
{
$filePathAndName = ProtectedResourceUtility::getStoragePathAndFilenameByHash($resource->getSha1(), $options['basePath']);

/** @var HttpResponseInterface $response */
$response = $httpResponse->withHeader('X-Sendfile', $filePathAndName);
return $response;

return $httpResponse->withHeader('X-Sendfile', $filePathAndName);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Wwwision\PrivateResources\Http\Component\Exception;
namespace Wwwision\PrivateResources\Http\Middleware\Exception;

/* *
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?php
namespace Wwwision\PrivateResources\Http\Component;
namespace Wwwision\PrivateResources\Http\Middleware;

/* *
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
* */

use GuzzleHttp\Psr7\Response;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException;
use Neos\Flow\Exception as FlowException;
use Neos\Flow\Http\Component\ComponentContext;
use Neos\Flow\Http\Component\ComponentInterface;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\ObjectManagement\DependencyInjection\DependencyProxy;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
Expand All @@ -22,17 +22,17 @@
use Neos\Flow\Security\Exception\NoSuchRoleException;
use Neos\Flow\Security\Policy\Role;
use Neos\Flow\Utility\Now;
use Neos\Utility\Files;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as HttpRequestInterface;
use Wwwision\PrivateResources\Http\Component\Exception\FileNotFoundException;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Wwwision\PrivateResources\Http\Middleware\Exception\FileNotFoundException;
use Wwwision\PrivateResources\Http\FileServeStrategy\FileServeStrategyInterface;
use Neos\Flow\Http\Component\ComponentChain;

/**
* A HTTP Component that checks for the request argument "__protectedResource" and outputs the requested resource if the client tokens match
* A HTTP Middleware that checks for the request argument "__protectedResource" and outputs the requested resource if the client tokens match
*/
class ProtectedResourceComponent implements ComponentInterface
class ProtectedResourceMiddleware implements MiddlewareInterface
{

/**
Expand Down Expand Up @@ -66,31 +66,30 @@ class ProtectedResourceComponent implements ComponentInterface
protected $now;

/**
* @Flow\InjectConfiguration(path="middleware")
* @var array
*/
protected $options;

/**
* @param array $options
*/
public function __construct(array $options = [])
{
$this->options = $options;
}

/**
* @param ComponentContext $componentContext
* @return void
* @throws FileNotFoundException|AccessDeniedException|FlowException
* @param HttpRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws FlowException
* @throws InvalidConfigurationTypeException
* @throws NoSuchRoleException
* @throws SecurityException
* @throws SecurityException\InvalidArgumentForHashGenerationException
*/
public function handle(ComponentContext $componentContext)
public function process(HttpRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
/** @var HttpRequestInterface $httpRequest */
$httpRequest = $componentContext->getHttpRequest();
$queryParams = $httpRequest->getQueryParams();
$queryParams = $request->getQueryParams();

if (!array_key_exists('__protectedResource', $queryParams) || $queryParams['__protectedResource'] === '' || $queryParams['__protectedResource'] === false) {
return;
return $handler->handle($request);
}
try {
$encodedResourceData = $this->hashService->validateAndStripHmac($queryParams['__protectedResource']);
Expand All @@ -99,8 +98,8 @@ public function handle(ComponentContext $componentContext)
}
$tokenData = json_decode(base64_decode($encodedResourceData), true);

$this->verifyExpiration($tokenData, $httpRequest);
$this->verifySecurityContext($tokenData, $httpRequest);
$this->verifyExpiration($tokenData, $request);
$this->verifySecurityContext($tokenData, $request);

$resource = $this->resourceManager->getResourceBySha1($tokenData['resourceIdentifier']);
if ($resource === null) {
Expand All @@ -117,27 +116,23 @@ public function handle(ComponentContext $componentContext)
get_class($fileServeStrategy)), 1429704284);
}
/** @var ResponseInterface $httpResponse */
$httpResponse = $componentContext->getHttpResponse()
->withHeader('Content-Type', $resource->getMediaType())
->withHeader('Content-Disposition', 'attachment;filename="' . $resource->getFilename() . '"')
->withHeader('Content-Length', $resource->getFileSize());

$this->emitResourceServed($resource, $httpRequest);
$httpResponse = new Response();
$httpResponse = $httpResponse->withHeader('Content-Type', $resource->getMediaType());
$httpResponse = $httpResponse->withHeader('Content-Disposition', 'attachment;filename="' . $resource->getFilename() . '"');
$httpResponse = $httpResponse->withHeader('Content-Length', $resource->getFileSize());

$httpResponse = $fileServeStrategy->serve($resource, $httpResponse, $this->options);
$componentContext->replaceHttpResponse($httpResponse);
$componentContext->setParameter(ComponentChain::class, 'cancel', true);
$this->emitResourceServed($resource, $request);
return $fileServeStrategy->serve($resource, $httpResponse, $this->options);
}

/**
* Checks whether the token is expired
*
* @param array $tokenData
* @param HttpRequestInterface $httpRequest
* @return void
* @throws AccessDeniedException
*/
protected function verifyExpiration(array $tokenData, HttpRequestInterface $httpRequest)
protected function verifyExpiration(array $tokenData, HttpRequestInterface $httpRequest): void
{
if (!isset($tokenData['expirationDateTime'])) {
return;
Expand All @@ -157,10 +152,12 @@ protected function verifyExpiration(array $tokenData, HttpRequestInterface $http
*
* @param array $tokenData
* @param HttpRequestInterface $httpRequest
* @return void
* @throws AccessDeniedException | SecurityException | NoSuchRoleException
* @throws AccessDeniedException
* @throws InvalidConfigurationTypeException
* @throws NoSuchRoleException
* @throws SecurityException
*/
protected function verifySecurityContext(array $tokenData, HttpRequestInterface $httpRequest)
protected function verifySecurityContext(array $tokenData, HttpRequestInterface $httpRequest): void
{
if (!isset($tokenData['securityContextHash']) && !isset($tokenData['privilegedRole'])) {
return;
Expand Down Expand Up @@ -191,7 +188,7 @@ protected function verifySecurityContext(array $tokenData, HttpRequestInterface
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitResourceServed(PersistentResource $resource, HttpRequestInterface $httpRequest)
protected function emitResourceServed(PersistentResource $resource, HttpRequestInterface $httpRequest): void
{
}

Expand All @@ -203,7 +200,7 @@ protected function emitResourceServed(PersistentResource $resource, HttpRequestI
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitAccessDenied(array $tokenData, HttpRequestInterface $httpRequest)
protected function emitAccessDenied(array $tokenData, HttpRequestInterface $httpRequest): void
{
}

Expand All @@ -216,7 +213,7 @@ protected function emitAccessDenied(array $tokenData, HttpRequestInterface $http
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitInvalidSecurityContextHash(array $tokenData, HttpRequestInterface $httpRequest)
protected function emitInvalidSecurityContextHash(array $tokenData, HttpRequestInterface $httpRequest): void
{
}
}
2 changes: 1 addition & 1 deletion Classes/Resource/Target/ProtectedResourceTarget.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ protected function getHttpRequest(): ?HttpRequestInterface
if (!$requestHandler instanceof HttpRequestHandlerInterface) {
return null;
}
$this->httpRequest = $requestHandler-> getComponentContext()->getHttpRequest();
$this->httpRequest = $requestHandler->getHttpRequest();
}
return $this->httpRequest;
}
Expand Down
8 changes: 2 additions & 6 deletions Classes/Utility/ProtectedResourceUtility.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
* This script belongs to the Neos Flow package "Wwwision.PrivateResources". *
* */

use Neos\Flow\Annotations as Flow;
use Neos\Utility\Files;
use Wwwision\PrivateResources\Http\Component\Exception\FileNotFoundException;
use Wwwision\PrivateResources\Http\Middleware\Exception\FileNotFoundException;

/**
* A HTTP Component that checks for the request argument "__protectedResource" and outputs the requested resource if the client tokens match
*/
class ProtectedResourceUtility
{

Expand All @@ -22,7 +18,7 @@ class ProtectedResourceUtility
* @return string
* @throws FileNotFoundException
*/
public static function getStoragePathAndFilenameByHash($sha1Hash, $basePath)
public static function getStoragePathAndFilenameByHash(string $sha1Hash, string $basePath): string
{
// TODO there should be a better way to determine the absolute path of the resource? Resource::createTemporaryLocalCopy() is too expensive
$resourcePathAndFilename = Files::concatenatePaths(
Expand Down
16 changes: 16 additions & 0 deletions Configuration/Settings.Http.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Neos:
Flow:
http:
middlewares:
'protectedResources':
position: 'before routing'
middleware: 'Wwwision\PrivateResources\Http\Middleware\ProtectedResourceMiddleware'

Wwwision:
PrivateResources:
middleware:
# absolute root path of resources
basePath: '%FLOW_PATH_DATA%Persistent/Resources/'
# how the file should be served (see README)
serveStrategy: 'Wwwision\PrivateResources\Http\FileServeStrategy\ReadfileStrategy'

14 changes: 0 additions & 14 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
Neos:
Flow:
http:
chain:
'process':
chain:
'protectedResources':
position: 'before routing'
component: 'Wwwision\PrivateResources\Http\Component\ProtectedResourceComponent'
componentOptions:
# absolute root path of resources
basePath: '%FLOW_PATH_DATA%Persistent/Resources/'

# how the file should be served (see README)
serveStrategy: 'Wwwision\PrivateResources\Http\FileServeStrategy\ReadfileStrategy'

resource:
targets:
'protectedResourcesTarget':
Expand Down
Loading

0 comments on commit e9a3474

Please sign in to comment.