Skip to content

Commit

Permalink
Merge pull request #16 from punktDeForks/task/flow-6-compat
Browse files Browse the repository at this point in the history
Add compatibility to flow 6.0 and psr7
  • Loading branch information
bwaidelich authored Jan 24, 2020
2 parents 84d4a3f + a531cc1 commit 493aa55
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 58 deletions.
47 changes: 25 additions & 22 deletions Classes/Http/Component/ProtectedResourceComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
use Neos\Flow\Exception as FlowException;
use Neos\Flow\Http\Component\ComponentContext;
use Neos\Flow\Http\Component\ComponentInterface;
use Neos\Flow\Http\Request as HttpRequest;
use Neos\Flow\Http\Response;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\ObjectManagement\DependencyInjection\DependencyProxy;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
Expand All @@ -25,6 +23,8 @@
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 Wwwision\PrivateResources\Http\FileServeStrategy\FileServeStrategyInterface;
use Neos\Flow\Http\Component\ComponentChain;
Expand Down Expand Up @@ -85,13 +85,15 @@ public function __construct(array $options = [])
*/
public function handle(ComponentContext $componentContext)
{
/** @var HttpRequest $httpRequest */
/** @var HttpRequestInterface $httpRequest */
$httpRequest = $componentContext->getHttpRequest();
if (!$httpRequest->hasArgument('__protectedResource')) {
$queryParams = $httpRequest->getQueryParams();

if (!array_key_exists('__protectedResource', $queryParams) || $queryParams['__protectedResource'] === '' || $queryParams['__protectedResource'] === false) {
return;
}
try {
$encodedResourceData = $this->hashService->validateAndStripHmac($httpRequest->getArgument('__protectedResource'));
$encodedResourceData = $this->hashService->validateAndStripHmac($queryParams['__protectedResource']);
} catch (InvalidHashException $exception) {
throw new AccessDeniedException('Invalid HMAC!', 1421241393, $exception);
}
Expand Down Expand Up @@ -128,27 +130,28 @@ public function handle(ComponentContext $componentContext)
throw new FlowException(sprintf('The class "%s" does not implement the FileServeStrategyInterface',
get_class($fileServeStrategy)), 1429704284);
}
/** @var Response $httpResponse */
$httpResponse = $componentContext->getHttpResponse();
$httpResponse->setHeader('Content-Type', $resource->getMediaType());
$httpResponse->setHeader('Content-Disposition', 'attachment;filename="' . $resource->getFilename() . '"');
$httpResponse->setHeader('Content-Length', $resource->getFileSize());
/** @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);

$fileServeStrategy->serve($resourcePathAndFilename, $httpResponse);
$httpResponse = $fileServeStrategy->serve($resourcePathAndFilename, $httpResponse);
$componentContext->replaceHttpResponse($httpResponse);
$componentContext->setParameter(ComponentChain::class, 'cancel', true);
}

/**
* Checks whether the token is expired
*
* @param array $tokenData
* @param HttpRequest $httpRequest
* @param HttpRequestInterface $httpRequest
* @return void
* @throws AccessDeniedException
*/
protected function verifyExpiration(array $tokenData, HttpRequest $httpRequest)
protected function verifyExpiration(array $tokenData, HttpRequestInterface $httpRequest)
{
if (!isset($tokenData['expirationDateTime'])) {
return;
Expand All @@ -167,16 +170,16 @@ protected function verifyExpiration(array $tokenData, HttpRequest $httpRequest)
* Checks whether the currently authenticated user is allowed to access the resource
*
* @param array $tokenData
* @param HttpRequest $httpRequest
* @param HttpRequestInterface $httpRequest
* @return void
* @throws AccessDeniedException | SecurityException | NoSuchRoleException
*/
protected function verifySecurityContext(array $tokenData, HttpRequest $httpRequest)
protected function verifySecurityContext(array $tokenData, HttpRequestInterface $httpRequest)
{
if (!isset($tokenData['securityContextHash']) && !isset($tokenData['privilegedRole'])) {
return;
}
$actionRequest = new ActionRequest($httpRequest);
$actionRequest = ActionRequest::fromHttpRequest($httpRequest);
$this->securityContext->setRequest($actionRequest);
if (isset($tokenData['privilegedRole'])) {
if ($this->securityContext->hasRole($tokenData['privilegedRole'])) {
Expand All @@ -199,10 +202,10 @@ protected function verifySecurityContext(array $tokenData, HttpRequest $httpRequ
*
* @Flow\Signal
* @param PersistentResource $resource the resource that has been served
* @param HttpRequest $httpRequest the current HTTP request
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitResourceServed(PersistentResource $resource, HttpRequest $httpRequest)
protected function emitResourceServed(PersistentResource $resource, HttpRequestInterface $httpRequest)
{
}

Expand All @@ -211,10 +214,10 @@ protected function emitResourceServed(PersistentResource $resource, HttpRequest
*
* @Flow\Signal
* @param array $tokenData the token data
* @param HttpRequest $httpRequest the current HTTP request
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitAccessDenied(array $tokenData, HttpRequest $httpRequest)
protected function emitAccessDenied(array $tokenData, HttpRequestInterface $httpRequest)
{
}

Expand All @@ -224,10 +227,10 @@ protected function emitAccessDenied(array $tokenData, HttpRequest $httpRequest)
* @Flow\Signal
* @deprecated use "accessDenied" signal instead
* @param array $tokenData the token data
* @param HttpRequest $httpRequest the current HTTP request
* @param HttpRequestInterface $httpRequest the current HTTP request
* @return void
*/
protected function emitInvalidSecurityContextHash(array $tokenData, HttpRequest $httpRequest)
protected function emitInvalidSecurityContextHash(array $tokenData, HttpRequestInterface $httpRequest)
{
}
}
7 changes: 4 additions & 3 deletions Classes/Http/FileServeStrategy/FileServeStrategyInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* */

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

/**
* Contract for a strategy that allows for serving files outside of the public folder structure
Expand All @@ -15,8 +16,8 @@ interface FileServeStrategyInterface

/**
* @param string $filePathAndName Absolute path to the file to serve
* @param HttpResponse $httpResponse The current HTTP response (allows setting headers, ...)
* @return void
* @param ResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @return ResponseInterface
*/
public function serve($filePathAndName, HttpResponse $httpResponse);
public function serve($filePathAndName, ResponseInterface $httpResponse): ResponseInterface;
}
20 changes: 8 additions & 12 deletions Classes/Http/FileServeStrategy/ReadfileStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* */

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Response as HttpResponse;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use function GuzzleHttp\Psr7\stream_for;

/**
* A file serve strategy that outputs the given file using PHPs readfile function
Expand All @@ -18,18 +19,13 @@ class ReadfileStrategy implements FileServeStrategyInterface

/**
* @param string $filePathAndName Absolute path to the file to serve
* @param HttpResponse $httpResponse The current HTTP response (allows setting headers, ...)
* @return void
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @return HttpResponseInterface
*/
public function serve($filePathAndName, HttpResponse $httpResponse)
public function serve($filePathAndName, HttpResponseInterface $httpResponse): HttpResponseInterface
{
$httpResponse->sendHeaders();
// Ensure no output buffer is used so the file contents won't be loaded into the RAM
// BTW: This does not work with xdebug enabled! (any output will be buffered by xdebug)
while (ob_get_level() > 0) {
ob_end_flush();
}
readfile($filePathAndName);
exit;
/** @var HttpResponseInterface $response */
$response = $httpResponse->withBody(stream_for(fopen($filePathAndName, 'rb')));
return $response;
}
}
12 changes: 7 additions & 5 deletions Classes/Http/FileServeStrategy/XAccelRedirectStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* */

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

/**
* A file serve strategy that uses the custom "X-accel-Redirect" header to let Nginx servers handle the file download.
Expand All @@ -21,11 +21,13 @@ class XAccelRedirectStrategy implements FileServeStrategyInterface

/**
* @param string $filePathAndName Absolute path to the file to serve
* @param HttpResponse $httpResponse The current HTTP response (allows setting headers, ...)
* @return void
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @return HttpResponseInterface
*/
public function serve($filePathAndName, HttpResponse $httpResponse)
public function serve($filePathAndName, HttpResponseInterface $httpResponse): HttpResponseInterface
{
$httpResponse->setHeader('X-Accel-Redirect', $filePathAndName);
/** @var HttpResponseInterface $response */
$response = $httpResponse->withHeader('X-Accel-Redirect', $filePathAndName);
return $response;
}
}
13 changes: 8 additions & 5 deletions Classes/Http/FileServeStrategy/XSendfileStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* */

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

/**
* A file serve strategy that uses the custom "X-Sendfile" header to let Apache servers handle the file download.
Expand All @@ -20,11 +20,14 @@ class XSendfileStrategy implements FileServeStrategyInterface

/**
* @param string $filePathAndName Absolute path to the file to serve
* @param HttpResponse $httpResponse The current HTTP response (allows setting headers, ...)
* @return void
* @param HttpResponseInterface $httpResponse The current HTTP response (allows setting headers, ...)
* @return HttpResponseInterface
*/
public function serve($filePathAndName, HttpResponse $httpResponse)
public function serve($filePathAndName, HttpResponseInterface $httpResponse): HttpResponseInterface
{
$httpResponse->setHeader('X-Sendfile', $filePathAndName);
/** @var HttpResponseInterface $response */
$response = $httpResponse->withHeader('X-Sendfile', $filePathAndName);
return $response;

}
}
23 changes: 13 additions & 10 deletions Classes/Resource/Target/ProtectedResourceTarget.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Http\Helper\RequestInformationHelper;
use Neos\Flow\Http\HttpRequestHandlerInterface;
use Neos\Flow\ResourceManagement\CollectionInterface;
use Neos\Flow\ResourceManagement\PersistentResource;
use Neos\Flow\ResourceManagement\Target\TargetInterface;
use Neos\Flow\Security\Context;
use Neos\Flow\Security\Cryptography\HashService;
use Neos\Flow\Utility\Now;
use Neos\Flow\Http\Request as HttpRequest;
use Psr\Http\Message\ServerRequestInterface as HttpRequestInterface;

/**
* A resource target that does not publish resources directly.
Expand Down Expand Up @@ -61,7 +62,7 @@ class ProtectedResourceTarget implements TargetInterface
protected $now;

/**
* @var HttpRequest
* @var HttpRequestInterface
*/
protected $httpRequest;

Expand Down Expand Up @@ -157,9 +158,11 @@ public function getPublicPersistentResourceUri(PersistentResource $resource)
}

/**
* @return boolean
* @return bool
* @throws \Neos\Flow\Security\Exception
* @throws \Neos\Flow\Security\Exception\NoSuchRoleException
*/
protected function shouldIncludeSecurityContext()
protected function shouldIncludeSecurityContext(): bool
{
if (!isset($this->options['whitelistRoles'])) {
return true;
Expand All @@ -177,26 +180,26 @@ protected function shouldIncludeSecurityContext()
*
* @return string The website's base URI
*/
protected function detectResourcesBaseUri()
protected function detectResourcesBaseUri(): string
{
$request = $this->getHttpRequest();
if (!$request instanceof HttpRequest) {
if (!$request instanceof HttpRequestInterface) {
return $this->defaultBaseUri;
}
return (string)$request->getBaseUri();
return (string)RequestInformationHelper::generateBaseUri($request);
}

/**
* @return HttpRequest
* @return HttpRequestInterface|null
*/
protected function getHttpRequest()
protected function getHttpRequest(): ?HttpRequestInterface
{
if ($this->httpRequest === null) {
$requestHandler = $this->bootstrap->getActiveRequestHandler();
if (!$requestHandler instanceof HttpRequestHandlerInterface) {
return null;
}
$this->httpRequest = $requestHandler->getHttpRequest();
$this->httpRequest = $requestHandler-> getComponentContext()->getHttpRequest();
}
return $this->httpRequest;
}
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "A Flow package that allows for protecting persistent resources from unauthorized access",
"license": "MIT",
"require": {
"neos/flow": "^4.1 || ^5.0"
"neos/flow": "^6.0",
"guzzlehttp/psr7": "^1.6"
},
"autoload": {
"psr-4": {
Expand Down

0 comments on commit 493aa55

Please sign in to comment.