Skip to content

Commit 0b9fc91

Browse files
committed
First commit.
0 parents  commit 0b9fc91

36 files changed

+2606
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
/vendor/
3+
composer.lock
4+
.phpunit.result.cache

Controller/ControllerMetadata.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Prokl\SymfonyMiddlewareBundle\Controller;
5+
6+
/**
7+
* Class ControllerMetadata
8+
* @package Prokl\SymfonyMiddlewareBundle\Controller
9+
*/
10+
final class ControllerMetadata
11+
{
12+
/**
13+
* @var string $controllerFqcn
14+
*/
15+
private $controllerFqcn;
16+
17+
/**
18+
* @var string $controllerAction
19+
*/
20+
private $controllerAction;
21+
22+
/**
23+
* ControllerMetadata constructor.
24+
*
25+
* @param string $controllerFqcn
26+
* @param string $controllerAction
27+
*/
28+
public function __construct(string $controllerFqcn, string $controllerAction)
29+
{
30+
$this->controllerFqcn = $controllerFqcn;
31+
$this->controllerAction = $controllerAction;
32+
}
33+
34+
/**
35+
* @return string
36+
*/
37+
public function getControllerFqcn(): string
38+
{
39+
return $this->controllerFqcn;
40+
}
41+
42+
/**
43+
* @return string
44+
*/
45+
public function getControllerAction(): string
46+
{
47+
return $this->controllerAction;
48+
}
49+
}

Controller/ControllerParser.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Prokl\SymfonyMiddlewareBundle\Controller;
5+
6+
/**
7+
* Class ControllerParser
8+
* @package Prokl\SymfonyMiddlewareBundle\Controller
9+
*/
10+
final class ControllerParser implements ControllerParserInterface
11+
{
12+
/**
13+
* @inheritDoc
14+
*/
15+
public function parse(callable $controller): ControllerMetadata
16+
{
17+
if (is_array($controller)) {
18+
return new ControllerMetadata(get_class($controller[0]), $controller[1]);
19+
}
20+
21+
return new ControllerMetadata(get_class($controller), '__invoke');
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Prokl\SymfonyMiddlewareBundle\Controller;
5+
6+
/**
7+
* Interface ControllerParserInterface
8+
* @package Prokl\SymfonyMiddlewareBundle\Controller
9+
*/
10+
interface ControllerParserInterface
11+
{
12+
/**
13+
* @param callable $controller
14+
*
15+
* @return ControllerMetadata
16+
*/
17+
public function parse(callable $controller): ControllerMetadata;
18+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware;
4+
5+
use Prokl\SymfonyMiddlewareBundle\ControllersMiddleware\Exceptions\WrongCsrfException;
6+
use Prokl\SymfonyMiddlewareBundle\MiddlewareInterface;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
use Symfony\Component\Security\Csrf\CsrfToken;
10+
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
11+
12+
/**
13+
* Class CsrfMiddleware
14+
* @package Local\Services\ControllerMiddleware
15+
*
16+
* @since 19.11.2020
17+
*/
18+
class CsrfMiddleware implements MiddlewareInterface
19+
{
20+
/**
21+
* @var CsrfTokenManagerInterface $csrfTokenManager CSRF токен менеджер.
22+
*/
23+
private $csrfTokenManager;
24+
25+
/**
26+
* CsrfMiddleware constructor.
27+
*
28+
* @param CsrfTokenManagerInterface $csrfTokenManager CSRF токен менеджер.
29+
*/
30+
public function __construct(CsrfTokenManagerInterface $csrfTokenManager)
31+
{
32+
$this->csrfTokenManager = $csrfTokenManager;
33+
}
34+
35+
/**
36+
* @inheritDoc
37+
*
38+
* @throws WrongCsrfException Невалидный токен.
39+
*/
40+
public function handle(Request $request): ?Response
41+
{
42+
$typeRequest = $request->server->get('REQUEST_METHOD');
43+
$token = $request->query->get('csrf_token');
44+
45+
if ($typeRequest !== 'GET') {
46+
$token = $request->request->get('csrf_token');
47+
}
48+
49+
$bValidToken = $this->csrfTokenManager->isTokenValid(
50+
new CsrfToken('app', $token)
51+
);
52+
53+
if (!$bValidToken) {
54+
throw new WrongCsrfException('Security error: Invalid CSRF token.');
55+
}
56+
57+
return null;
58+
}
59+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware;
4+
5+
use Prokl\SymfonyMiddlewareBundle\MiddlewareInterface;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
9+
/**
10+
* Class ExampleMiddleware
11+
* @package Local\Services\ControllerMiddleware
12+
*
13+
* @since 19.11.2020
14+
*/
15+
class ExampleMiddleware implements MiddlewareInterface
16+
{
17+
public function handle(Request $request): ?Response
18+
{
19+
if ($request->query->get('test')) {
20+
return new Response('OK2');
21+
}
22+
return null;
23+
}
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware\Exceptions;
4+
5+
use Prokl\BaseException\BaseException;
6+
7+
/**
8+
* Class NotAllowedTypeRequestException
9+
* @package Local\Services\ControllerMiddleware\Exceptions
10+
*
11+
* @since 19.11.2020
12+
*/
13+
class NotAllowedTypeRequestException extends BaseException
14+
{
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware\Exceptions;
4+
5+
use Prokl\BaseException\BaseException;
6+
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
7+
8+
/**
9+
* Class WrongCsrfException
10+
* @package Local\Services\ControllerMiddleware\Exceptions
11+
*
12+
* @since 19.11.2020
13+
*/
14+
class WrongCsrfException extends BaseException implements RequestExceptionInterface
15+
{
16+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware;
4+
5+
use Prokl\SymfonyMiddlewareBundle\MiddlewareInterface;
6+
use Prokl\SymfonyMiddlewareBundle\ControllersMiddleware\Exceptions\NotAllowedTypeRequestException;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
10+
/**
11+
* Class OnlyAjaxMiddleware
12+
* Only AJAX call allowed.
13+
* @package Local\Services\ControllerMiddleware
14+
*
15+
* @since 19.11.2020
16+
*/
17+
class OnlyAjaxMiddleware implements MiddlewareInterface
18+
{
19+
/**
20+
* @inheritDoc
21+
*
22+
* @throws NotAllowedTypeRequestException
23+
*/
24+
public function handle(Request $request): ?Response
25+
{
26+
if (!$request->isXmlHttpRequest()) {
27+
throw new NotAllowedTypeRequestException(
28+
'Only AJAX call allowed.'
29+
);
30+
}
31+
32+
return null;
33+
}
34+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
namespace Prokl\SymfonyMiddlewareBundle\ControllersMiddleware;
4+
5+
use Prokl\SymfonyMiddlewareBundle\ControllersMiddleware\Utils\ContentTypeMapping;
6+
use Prokl\SymfonyMiddlewareBundle\MiddlewareInterface;
7+
use LogicException;
8+
use Symfony\Component\HttpFoundation\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
use Symfony\Component\HttpFoundation\StreamedResponse;
11+
12+
/**
13+
* Class StaticFileMiddleware
14+
* @package Local\Services\ControllerMiddleware
15+
*
16+
* @since 23.02.2021
17+
*/
18+
class StaticFileMiddleware implements MiddlewareInterface
19+
{
20+
/**
21+
* @var string $publicDirectory Директория, где лежат файлы.
22+
*/
23+
private $publicDirectory;
24+
25+
/**
26+
* @var string $hashAlgorithm Алгоритм хэширования.
27+
*/
28+
private $hashAlgorithm;
29+
30+
/**
31+
* StaticFileMiddleware constructor.
32+
*
33+
* @param string $publicDirectory Директория, где лежат файлы.
34+
* @param string $hashAlgorithm Алгоритм хэширования.
35+
*/
36+
public function __construct(
37+
string $publicDirectory = '',
38+
string $hashAlgorithm = 'md5'
39+
) {
40+
$this->publicDirectory = $publicDirectory;
41+
$this->hashAlgorithm = $hashAlgorithm;
42+
}
43+
44+
/**
45+
* @inheritDoc
46+
*/
47+
public function handle(Request $request): ?Response
48+
{
49+
if (!in_array($this->hashAlgorithm, hash_algos(), true)) {
50+
throw new LogicException(sprintf('Invalid or not supported hash algorithm: "%s"', $this->hashAlgorithm));
51+
}
52+
53+
if ($this->publicDirectory === '') {
54+
throw new LogicException(
55+
sprintf('Public upload directory not configured. You forget initialize StaticFileMiddleware as service?')
56+
);
57+
}
58+
59+
$target = $this->getRequestTarget($request);
60+
if ($target === '') {
61+
return null;
62+
}
63+
64+
$filename = $this->publicDirectory . $target;
65+
66+
$hash = hash_file($this->hashAlgorithm, $filename);
67+
68+
if (!is_readable($filename)) {
69+
return null;
70+
}
71+
72+
$response = new StreamedResponse();
73+
74+
if ($request->headers->get('If-None-Match') === $hash) {
75+
$response->setStatusCode(304);
76+
}
77+
78+
$response->headers->set('Content-Length', (string) filesize($filename));
79+
$response->headers->set(
80+
'Content-Disposition',
81+
'attachment; filename="'.$target.'"'
82+
);
83+
$response->headers->set('ETag', $hash);
84+
85+
$this->addContentType($response, $target);
86+
87+
$response->setCallback(function () use ($filename) {
88+
echo (string)file_get_contents($filename);
89+
});
90+
91+
return $response;
92+
}
93+
94+
/**
95+
* @param Request $request Request.
96+
*
97+
* @return string
98+
*/
99+
private function getRequestTarget(Request $request) : string
100+
{
101+
$target = '';
102+
103+
if ($request->getQueryString()) {
104+
$target = $request->getQueryString();
105+
parse_str($target, $result);
106+
$target = (string)$result['file'];
107+
}
108+
109+
return $target;
110+
}
111+
112+
/**
113+
* Добавить content type.
114+
*
115+
* @param Response $response Response.
116+
* @param string $filename Имя файла.
117+
*
118+
* @return Response
119+
*/
120+
private function addContentType(Response $response, string $filename): Response
121+
{
122+
$extension = pathinfo($filename, PATHINFO_EXTENSION);
123+
if (array_key_exists($extension, ContentTypeMapping::CONTENT_TYPE_MAPPING)) {
124+
$response->headers->set('Content-Type', ContentTypeMapping::CONTENT_TYPE_MAPPING[$extension]);
125+
}
126+
127+
return $response;
128+
}
129+
}

0 commit comments

Comments
 (0)