Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
83.12% |
128 / 154 |
|
50.00% |
6 / 12 |
CRAP | |
0.00% |
0 / 1 |
LogJournalDoctrineSubscriber | |
83.12% |
128 / 154 |
|
50.00% |
6 / 12 |
56.18 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getSubscribedEvents | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
postPersist | |
40.00% |
6 / 15 |
|
0.00% |
0 / 1 |
13.78 | |||
postUpdate | |
42.86% |
9 / 21 |
|
0.00% |
0 / 1 |
50.57 | |||
preRemove | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
registerLog | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
3 | |||
registerDeleteLog | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
2.00 | |||
supports | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getCollectivity | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
registerLogForUser | |
95.00% |
38 / 40 |
|
0.00% |
0 / 1 |
9 | |||
registerLogSoftDelete | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
2.00 | |||
notConcernedByDeletionLog | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * This file is part of the MADIS - RGPD Management application. |
5 | * |
6 | * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes |
7 | * |
8 | * This program is free software: you can redistribute it and/or modify |
9 | * it under the terms of the GNU Affero General Public License as published by |
10 | * the Free Software Foundation, either version 3 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU Affero General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Affero General Public License |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | declare(strict_types=1); |
23 | |
24 | namespace App\Domain\Reporting\Symfony\EventSubscriber\Doctrine; |
25 | |
26 | use App\Domain\Registry\Model\ConformiteOrganisation\Conformite; |
27 | use App\Domain\Registry\Model\ConformiteOrganisation\Participant; |
28 | use App\Domain\Registry\Model\ConformiteTraitement\Reponse; |
29 | use App\Domain\Registry\Model\Proof; |
30 | use App\Domain\Registry\Model\Request; |
31 | use App\Domain\Registry\Model\Violation; |
32 | use App\Domain\Reporting\Dictionary\LogJournalActionDictionary; |
33 | use App\Domain\Reporting\Dictionary\LogJournalSubjectDictionary; |
34 | use App\Domain\Reporting\Model\LoggableSubject; |
35 | use App\Domain\Reporting\Model\LogJournal; |
36 | use App\Domain\Reporting\Symfony\EventSubscriber\Event\LogJournalEvent; |
37 | use App\Domain\User\Model\Collectivity; |
38 | use App\Domain\User\Model\ComiteIlContact; |
39 | use App\Domain\User\Model\Service; |
40 | use App\Domain\User\Model\User; |
41 | use Doctrine\Common\EventSubscriber; |
42 | use Doctrine\ORM\EntityManagerInterface; |
43 | use Doctrine\ORM\Event\LifecycleEventArgs; |
44 | use Doctrine\ORM\Events; |
45 | use Symfony\Component\Cache\Adapter\AdapterInterface; |
46 | use Symfony\Component\Cache\Adapter\ArrayAdapter; |
47 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
48 | use Symfony\Component\HttpFoundation\RequestStack; |
49 | use Symfony\Component\Security\Core\Security; |
50 | |
51 | class LogJournalDoctrineSubscriber implements EventSubscriber |
52 | { |
53 | /** |
54 | * @var Security |
55 | */ |
56 | private $security; |
57 | |
58 | /** |
59 | * @var EventDispatcherInterface |
60 | */ |
61 | private $eventDispatcher; |
62 | |
63 | /** |
64 | * @var EntityManagerInterface |
65 | */ |
66 | private $entityManager; |
67 | |
68 | /** |
69 | * To avoid multiple line on same object register objects in folder. |
70 | * |
71 | * @var AdapterInterface |
72 | */ |
73 | private $cacheAdapter; |
74 | |
75 | /** |
76 | * @var RequestStack |
77 | */ |
78 | private $requestStack; |
79 | |
80 | public function __construct( |
81 | Security $security, |
82 | EventDispatcherInterface $eventDispatcher, |
83 | EntityManagerInterface $entityManager, |
84 | ArrayAdapter $cacheAdapter, |
85 | RequestStack $requestStack, |
86 | ) { |
87 | $this->security = $security; |
88 | $this->eventDispatcher = $eventDispatcher; |
89 | $this->entityManager = $entityManager; |
90 | $this->cacheAdapter = $cacheAdapter; |
91 | $this->requestStack = $requestStack; |
92 | } |
93 | |
94 | public function getSubscribedEvents() |
95 | { |
96 | return [ |
97 | Events::postPersist, |
98 | Events::postUpdate, |
99 | Events::preRemove, |
100 | ]; |
101 | } |
102 | |
103 | public function postPersist(LifecycleEventArgs $args): void |
104 | { |
105 | $object = $args->getObject(); |
106 | if (!$this->supports($object)) { |
107 | return; |
108 | } |
109 | |
110 | $action = LogJournalActionDictionary::CREATE; |
111 | |
112 | switch (\get_class($args->getObject())) { |
113 | // l'ajout d'un participant ou d'une conformité entraine la modification de l'évaluation |
114 | case Conformite::class: |
115 | case Participant::class: |
116 | $object = $object->getEvaluation(); |
117 | $action = LogJournalActionDictionary::UPDATE; |
118 | break; |
119 | // l'ajout d'un contact IL ou d'un service entraine la modification de la structure |
120 | case ComiteIlContact::class: |
121 | $object = $object->getCollectivity(); |
122 | $action = LogJournalActionDictionary::UPDATE; |
123 | break; |
124 | // l'ajout d'une réponse entraine la modification de la conformité du traitement |
125 | case Reponse::class: |
126 | $object = $object->getConformiteTraitement(); |
127 | $action = LogJournalActionDictionary::UPDATE; |
128 | break; |
129 | } |
130 | |
131 | $this->registerLog($object, $action); |
132 | } |
133 | |
134 | public function postUpdate(LifecycleEventArgs $args): void |
135 | { |
136 | $object = $args->getObject(); |
137 | if (!$this->supports($object)) { |
138 | return; |
139 | } |
140 | |
141 | // specific case for user. Need to know which data is update |
142 | switch (\get_class($args->getObject())) { |
143 | case User::class: |
144 | $this->registerLogForUser($object); |
145 | break; |
146 | case Proof::class: |
147 | case Request::class: |
148 | case Violation::class: |
149 | $this->registerLogSoftDelete($object); |
150 | break; |
151 | case Conformite::class: |
152 | case Participant::class: |
153 | $this->registerLog($object->getEvaluation(), LogJournalActionDictionary::UPDATE); |
154 | break; |
155 | case ComiteIlContact::class: |
156 | $this->registerLog($object->getCollectivity(), LogJournalActionDictionary::UPDATE); |
157 | break; |
158 | case Reponse::class: |
159 | /** @var User $user */ |
160 | $user = $this->security->getUser(); |
161 | $item = $this->cacheAdapter->getItem('already_register_item-' . $user->getId()->toString()); |
162 | $request = $this->requestStack->getCurrentRequest(); |
163 | $conformite = $object->getConformiteTraitement(); |
164 | $id = $request->get('id'); |
165 | // Cas spéciale lors de l'édition du traitement lors de la modification d'une conformité d'un traitement |
166 | if (\is_null($item->get()) || $id === $item->get()->toString() && $conformite->getId()->toString() === $id) { |
167 | $this->registerLog($conformite, LogJournalActionDictionary::UPDATE); |
168 | } |
169 | break; |
170 | default: |
171 | $this->registerLog($object, LogJournalActionDictionary::UPDATE); |
172 | } |
173 | } |
174 | |
175 | public function preRemove(LifecycleEventArgs $args): void |
176 | { |
177 | if (!$this->supports($args->getObject())) { |
178 | return; |
179 | } |
180 | |
181 | $this->registerDeleteLog($args); |
182 | } |
183 | |
184 | private function registerLog(LoggableSubject $object, string $action) |
185 | { |
186 | /** @var User $user */ |
187 | $user = $this->security->getUser(); |
188 | $item = $this->cacheAdapter->getItem('already_register_item-' . $user->getId()->toString()); |
189 | // si la clef de l'objet existe déja alors on ajoute pas de log. |
190 | if (!$item->isHit()) { |
191 | $item->expiresAfter(2); |
192 | $item->set($object->getId()); |
193 | $this->cacheAdapter->save($item); |
194 | } elseif ($object->getId()->toString() === $item->get()->toString()) { |
195 | return; |
196 | } |
197 | |
198 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
199 | |
200 | $log = new LogJournal( |
201 | $this->getCollectivity($object), |
202 | $user->getFullName(), |
203 | $user->getEmail(), |
204 | $action, |
205 | $subject, |
206 | $object->getId()->toString(), |
207 | $object->__toString() |
208 | ); |
209 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
210 | } |
211 | |
212 | private function registerDeleteLog(LifecycleEventArgs $args) |
213 | { |
214 | $object = $args->getObject(); |
215 | |
216 | if ($this->notConcernedByDeletionLog($object)) { |
217 | return; |
218 | } |
219 | |
220 | /** @var User $user */ |
221 | $user = $this->security->getUser(); |
222 | |
223 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
224 | |
225 | $log = new LogJournal( |
226 | $this->getCollectivity($object), |
227 | $user->getFullName(), |
228 | $user->getEmail(), |
229 | LogJournalActionDictionary::DELETE, |
230 | $subject, |
231 | $object->getId()->toString(), |
232 | $object->__toString() |
233 | ); |
234 | |
235 | $this->eventDispatcher->dispatch(new LogJournalEvent($log, $object)); |
236 | } |
237 | |
238 | private function supports($object): bool |
239 | { |
240 | return $object instanceof LoggableSubject && !\is_null($this->security->getUser()); |
241 | } |
242 | |
243 | private function getCollectivity($object) |
244 | { |
245 | if (Collectivity::class === \get_class($object)) { |
246 | return $object; |
247 | } |
248 | |
249 | if (\method_exists($object, 'getCollectivity')) { |
250 | return $object->getCollectivity(); |
251 | } |
252 | |
253 | /** @var User $user */ |
254 | $user = $this->security->getUser(); |
255 | |
256 | return $user->getCollectivity(); |
257 | } |
258 | |
259 | private function registerLogForUser(LoggableSubject $object) |
260 | { |
261 | /** @var User $user */ |
262 | $user = $this->security->getUser(); |
263 | $changes = $this->entityManager->getUnitOfWork()->getEntityChangeSet($object); |
264 | |
265 | // don't need to add log on lastLogin because already register in LoginSubscriber |
266 | if (\array_key_exists('lastlogin', $changes)) { |
267 | return; |
268 | } |
269 | |
270 | $collectivity = $this->getCollectivity($object); |
271 | if (\array_key_exists('deletedAt', $changes)) { |
272 | $action = LogJournalActionDictionary::SOFT_DELETE; |
273 | if (!\is_null($changes['deletedAt'][0])) { |
274 | $action = LogJournalActionDictionary::SOFT_DELETE_REVOKE; |
275 | } |
276 | $log = new LogJournal( |
277 | $collectivity, |
278 | $user->getFullName(), |
279 | $user->getEmail(), |
280 | $action, |
281 | LogJournalSubjectDictionary::USER_USER, |
282 | $object->getId()->toString(), |
283 | $object->__toString() |
284 | ); |
285 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
286 | |
287 | return; |
288 | } |
289 | |
290 | $subjectTypes = []; |
291 | if (\array_key_exists('firstName', $changes)) { |
292 | $subjectTypes[] = LogJournalSubjectDictionary::USER_FIRSTNAME; |
293 | } |
294 | |
295 | if (\array_key_exists('lastName', $changes)) { |
296 | $subjectTypes[] = LogJournalSubjectDictionary::USER_LASTNAME; |
297 | } |
298 | |
299 | if (\array_key_exists('email', $changes)) { |
300 | $subjectTypes[] = LogJournalSubjectDictionary::USER_EMAIL; |
301 | } |
302 | |
303 | if (\array_key_exists('password', $changes)) { |
304 | $subjectTypes[] = LogJournalSubjectDictionary::USER_PASSWORD; |
305 | } |
306 | |
307 | foreach ($subjectTypes as $subjectType) { |
308 | $log = new LogJournal( |
309 | $collectivity, |
310 | $user->getFullName(), |
311 | $user->getEmail(), |
312 | LogJournalActionDictionary::UPDATE, |
313 | $subjectType, |
314 | $object->getId()->toString(), |
315 | $object->__toString() |
316 | ); |
317 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
318 | } |
319 | } |
320 | |
321 | private function registerLogSoftDelete(LoggableSubject $object) |
322 | { |
323 | /** @var User $user */ |
324 | $user = $this->security->getUser(); |
325 | $changes = $this->entityManager->getUnitOfWork()->getEntityChangeSet($object); |
326 | $collectivity = $this->getCollectivity($object); |
327 | |
328 | // if is not a softdelete request, just register a log update |
329 | $action = LogJournalActionDictionary::UPDATE; |
330 | if (\array_key_exists('deletedAt', $changes)) { |
331 | $action = LogJournalActionDictionary::SOFT_DELETE; |
332 | } |
333 | |
334 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
335 | $log = new LogJournal( |
336 | $collectivity, |
337 | $user->getFullName(), |
338 | $user->getEmail(), |
339 | $action, |
340 | $subject, |
341 | $object->getId()->toString(), |
342 | $object->__toString() |
343 | ); |
344 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
345 | } |
346 | |
347 | private function notConcernedByDeletionLog(LoggableSubject $subject): bool |
348 | { |
349 | return \in_array(\get_class($subject), [ |
350 | Conformite::class, |
351 | Participant::class, |
352 | ComiteIlContact::class, |
353 | Service::class, |
354 | Reponse::class, |
355 | ]); |
356 | } |
357 | } |