1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace SlayerBirden\DataFlowServer\Authorization\Controller; |
5
|
|
|
|
6
|
|
|
use Doctrine\Common\Collections\Criteria; |
7
|
|
|
use Doctrine\ORM\EntityManager; |
8
|
|
|
use Doctrine\ORM\ORMException; |
9
|
|
|
use Psr\Http\Message\ResponseInterface; |
10
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
11
|
|
|
use Psr\Http\Server\MiddlewareInterface; |
12
|
|
|
use Psr\Http\Server\RequestHandlerInterface; |
13
|
|
|
use Psr\Log\LoggerInterface; |
14
|
|
|
use SlayerBirden\DataFlowServer\Authorization\Entities\Permission; |
15
|
|
|
use SlayerBirden\DataFlowServer\Authorization\HistoryManagementInterface; |
16
|
|
|
use SlayerBirden\DataFlowServer\Doctrine\Middleware\ResourceMiddlewareInterface; |
17
|
|
|
use SlayerBirden\DataFlowServer\Domain\Entities\ClaimedResourceInterface; |
18
|
|
|
use SlayerBirden\DataFlowServer\Domain\Entities\User; |
19
|
|
|
use SlayerBirden\DataFlowServer\Notification\DangerMessage; |
20
|
|
|
use SlayerBirden\DataFlowServer\Notification\SuccessMessage; |
21
|
|
|
use SlayerBirden\DataFlowServer\Stdlib\Validation\ValidationResponseFactory; |
22
|
|
|
use Zend\Diactoros\Response\JsonResponse; |
23
|
|
|
use Zend\Hydrator\HydratorInterface; |
24
|
|
|
use Zend\InputFilter\InputFilterInterface; |
25
|
|
|
|
26
|
|
|
class SavePermissionsAction implements MiddlewareInterface |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* @var EntityManager |
30
|
|
|
*/ |
31
|
|
|
private $entityManager; |
32
|
|
|
/** |
33
|
|
|
* @var LoggerInterface |
34
|
|
|
*/ |
35
|
|
|
private $logger; |
36
|
|
|
/** |
37
|
|
|
* @var InputFilterInterface |
38
|
|
|
*/ |
39
|
|
|
private $inputFilter; |
40
|
|
|
/** |
41
|
|
|
* @var HistoryManagementInterface |
42
|
|
|
*/ |
43
|
|
|
private $historyManagement; |
44
|
|
|
/** |
45
|
|
|
* @var HydratorInterface |
46
|
|
|
*/ |
47
|
|
|
private $hydrator; |
48
|
|
|
|
49
|
|
|
public function __construct( |
50
|
|
|
EntityManager $entityManager, |
|
|
|
|
51
|
|
|
LoggerInterface $logger, |
52
|
|
|
InputFilterInterface $inputFilter, |
53
|
|
|
HistoryManagementInterface $historyManagement, |
54
|
|
|
HydratorInterface $hydrator |
55
|
|
|
) { |
56
|
|
|
$this->entityManager = $entityManager; |
57
|
|
|
$this->logger = $logger; |
58
|
|
|
$this->inputFilter = $inputFilter; |
59
|
|
|
$this->historyManagement = $historyManagement; |
60
|
|
|
$this->hydrator = $hydrator; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @inheritdoc |
65
|
|
|
*/ |
66
|
|
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface |
67
|
|
|
{ |
68
|
|
|
$data = $request->getParsedBody(); |
69
|
|
|
$user = $request->getAttribute(ResourceMiddlewareInterface::DATA_RESOURCE); |
70
|
|
|
|
71
|
|
|
$this->inputFilter->setData($data); |
|
|
|
|
72
|
|
|
if (!$this->inputFilter->isValid()) { |
73
|
|
|
return (new ValidationResponseFactory())('permissions', $this->inputFilter, []); |
74
|
|
|
} |
75
|
|
|
$this->entityManager->beginTransaction(); |
76
|
|
|
try { |
77
|
|
|
$permissions = $this->processResources( |
78
|
|
|
$user, |
79
|
|
|
$data[ClaimedResourceInterface::OWNER_PARAM], |
80
|
|
|
...$data['resources'] |
81
|
|
|
); |
82
|
|
|
if (empty($permissions)) { |
83
|
|
|
$msg = new SuccessMessage('No changes detected. The input is identical to the storage.'); |
84
|
|
|
} else { |
85
|
|
|
$this->entityManager->flush(); |
86
|
|
|
$this->entityManager->commit(); |
87
|
|
|
$msg = new SuccessMessage('Successfully set permissions to resources.'); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return new JsonResponse([ |
91
|
|
|
'msg' => $msg, |
92
|
|
|
'data' => [ |
93
|
|
|
'permissions' => array_map([$this->hydrator, 'extract'], $permissions), |
94
|
|
|
], |
95
|
|
|
'success' => true, |
96
|
|
|
], 200); |
97
|
|
|
} catch (ORMException $exception) { |
98
|
|
|
$this->logger->error((string)$exception); |
99
|
|
|
$this->entityManager->rollback(); |
100
|
|
|
|
101
|
|
|
return new JsonResponse([ |
102
|
|
|
'msg' => new DangerMessage('There was an error while setting the permissions.'), |
103
|
|
|
'data' => [ |
104
|
|
|
'permissions' => [], |
105
|
|
|
], |
106
|
|
|
'success' => false, |
107
|
|
|
], 400); |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param User $user |
113
|
|
|
* @param User $owner |
114
|
|
|
* @param string ...$resources |
115
|
|
|
* @return Permission[] |
116
|
|
|
* @throws ORMException |
117
|
|
|
*/ |
118
|
|
|
private function processResources(User $user, User $owner, string ...$resources): array |
119
|
|
|
{ |
120
|
|
|
$result = []; |
121
|
|
|
$collection = $this->entityManager |
122
|
|
|
->getRepository(Permission::class) |
123
|
|
|
->matching(Criteria::create()->where(Criteria::expr()->eq('user', $user))); |
124
|
|
|
|
125
|
|
|
$currentResources = array_map(function (Permission $permission) { |
126
|
|
|
return $permission->getResource(); |
127
|
|
|
}, $collection->toArray()); |
128
|
|
|
|
129
|
|
|
$toRemove = array_diff($currentResources, $resources); |
130
|
|
|
$toAdd = array_diff($resources, $currentResources); |
131
|
|
|
|
132
|
|
|
if (empty($toAdd) && empty($toRemove)) { |
133
|
|
|
return []; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
$this->processItemsToRemove($collection, $toRemove, $owner, $result); |
137
|
|
|
$this->processItemsToAdd($toAdd, $user, $owner, $result); |
138
|
|
|
|
139
|
|
|
return $result; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @param $collection |
144
|
|
|
* @param $toRemove |
145
|
|
|
* @param $owner |
146
|
|
|
* @param $result |
147
|
|
|
* @throws ORMException |
148
|
|
|
*/ |
149
|
|
|
private function processItemsToRemove($collection, $toRemove, $owner, &$result): void |
150
|
|
|
{ |
151
|
|
|
/** @var Permission $permission */ |
152
|
|
|
foreach ($collection as $permission) { |
153
|
|
|
if (in_array($permission->getResource(), $toRemove, true)) { |
154
|
|
|
$this->entityManager->remove($permission); |
155
|
|
|
$history = $this->historyManagement->fromPermission($permission); |
156
|
|
|
$history->setOwner($owner); |
157
|
|
|
$this->entityManager->persist($history); |
158
|
|
|
|
|
|
|
|
159
|
|
|
} else { |
160
|
|
|
$result[] = $permission; |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @param $toAdd |
167
|
|
|
* @param $user |
168
|
|
|
* @param $owner |
169
|
|
|
* @param $result |
170
|
|
|
* @throws ORMException |
171
|
|
|
*/ |
172
|
|
|
private function processItemsToAdd($toAdd, $user, $owner, &$result): void |
173
|
|
|
{ |
174
|
|
|
foreach ($toAdd as $resource) { |
175
|
|
|
$permission = new Permission(); |
176
|
|
|
$permission->setResource($resource); |
177
|
|
|
$permission->setUser($user); |
178
|
|
|
$result[] = $permission; |
179
|
|
|
$this->entityManager->persist($permission); |
180
|
|
|
$history = $this->historyManagement->fromPermission($permission); |
181
|
|
|
$history->setOwner($owner); |
182
|
|
|
$this->entityManager->persist($history); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.