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\Domain\Entities\User; |
17
|
|
|
use SlayerBirden\DataFlowServer\Notification\DangerMessage; |
18
|
|
|
use SlayerBirden\DataFlowServer\Notification\SuccessMessage; |
19
|
|
|
use Zend\Diactoros\Response\JsonResponse; |
20
|
|
|
use Zend\Hydrator\ExtractionInterface; |
21
|
|
|
use Zend\InputFilter\InputFilterInterface; |
22
|
|
|
|
23
|
|
|
class SavePermissionsAction implements MiddlewareInterface |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @var EntityManager |
27
|
|
|
*/ |
28
|
|
|
private $entityManager; |
29
|
|
|
/** |
30
|
|
|
* @var LoggerInterface |
31
|
|
|
*/ |
32
|
|
|
private $logger; |
33
|
|
|
/** |
34
|
|
|
* @var InputFilterInterface |
35
|
|
|
*/ |
36
|
|
|
private $inputFilter; |
37
|
|
|
/** |
38
|
|
|
* @var HistoryManagementInterface |
39
|
|
|
*/ |
40
|
|
|
private $historyManagement; |
41
|
|
|
/** |
42
|
|
|
* @var ExtractionInterface |
43
|
|
|
*/ |
44
|
|
|
private $extraction; |
45
|
|
|
|
46
|
|
|
public function __construct( |
47
|
|
|
EntityManager $entityManager, |
|
|
|
|
48
|
|
|
LoggerInterface $logger, |
49
|
|
|
InputFilterInterface $inputFilter, |
50
|
|
|
HistoryManagementInterface $historyManagement, |
51
|
|
|
ExtractionInterface $extraction |
52
|
|
|
) { |
53
|
|
|
$this->entityManager = $entityManager; |
54
|
|
|
$this->logger = $logger; |
55
|
|
|
$this->inputFilter = $inputFilter; |
56
|
|
|
$this->historyManagement = $historyManagement; |
57
|
|
|
$this->extraction = $extraction; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @inheritdoc |
62
|
|
|
*/ |
63
|
|
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface |
64
|
|
|
{ |
65
|
|
|
$data = $request->getParsedBody(); |
66
|
|
|
$userId = (int)$request->getAttribute('id'); |
67
|
|
|
|
68
|
|
|
$this->entityManager->beginTransaction(); |
69
|
|
|
try { |
70
|
|
|
/** @var User $user */ |
71
|
|
|
$user = $this->entityManager->find(User::class, $userId); |
72
|
|
|
if (! $user) { |
73
|
|
|
return new JsonResponse([ |
74
|
|
|
'msg' => new DangerMessage('Could not find user by provided ID.'), |
75
|
|
|
'data' => [ |
76
|
|
|
'permissions' => [], |
77
|
|
|
], |
78
|
|
|
'success' => false, |
79
|
|
|
], 400); |
80
|
|
|
} |
81
|
|
|
$permissions = $this->processResources($user, $data['owner'], ...$data['resources']); |
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->extraction, '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) |
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) |
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.