Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
81.82% |
126 / 154 |
|
41.67% |
5 / 12 |
CRAP | |
0.00% |
0 / 1 |
LogJournalDoctrineSubscriber | |
81.82% |
126 / 154 |
|
41.67% |
5 / 12 |
61.85 | |
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 |
56.98 | |||
preRemove | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
registerLog | |
89.47% |
17 / 19 |
|
0.00% |
0 / 1 |
3.01 | |||
registerDeleteLog | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
3.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\PostPersistEventArgs; |
44 | use Doctrine\ORM\Event\PostUpdateEventArgs; |
45 | use Doctrine\ORM\Event\PreRemoveEventArgs; |
46 | use Doctrine\ORM\Events; |
47 | use Symfony\Component\Cache\Adapter\AdapterInterface; |
48 | use Symfony\Component\Cache\Adapter\ArrayAdapter; |
49 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
50 | use Symfony\Component\HttpFoundation\RequestStack; |
51 | use Symfony\Component\Security\Core\Security; |
52 | |
53 | class LogJournalDoctrineSubscriber implements EventSubscriber |
54 | { |
55 | /** |
56 | * @var Security |
57 | */ |
58 | private $security; |
59 | |
60 | /** |
61 | * @var EventDispatcherInterface |
62 | */ |
63 | private $eventDispatcher; |
64 | |
65 | /** |
66 | * @var EntityManagerInterface |
67 | */ |
68 | private $entityManager; |
69 | |
70 | /** |
71 | * To avoid multiple line on same object register objects in folder. |
72 | * |
73 | * @var AdapterInterface |
74 | */ |
75 | private $cacheAdapter; |
76 | |
77 | /** |
78 | * @var RequestStack |
79 | */ |
80 | private $requestStack; |
81 | |
82 | public function __construct( |
83 | Security $security, |
84 | EventDispatcherInterface $eventDispatcher, |
85 | EntityManagerInterface $entityManager, |
86 | ArrayAdapter $cacheAdapter, |
87 | RequestStack $requestStack, |
88 | ) { |
89 | $this->security = $security; |
90 | $this->eventDispatcher = $eventDispatcher; |
91 | $this->entityManager = $entityManager; |
92 | $this->cacheAdapter = $cacheAdapter; |
93 | $this->requestStack = $requestStack; |
94 | } |
95 | |
96 | public function getSubscribedEvents(): array |
97 | { |
98 | return [ |
99 | Events::postPersist, |
100 | Events::postUpdate, |
101 | Events::preRemove, |
102 | ]; |
103 | } |
104 | |
105 | public function postPersist(PostPersistEventArgs $args): void |
106 | { |
107 | $object = $args->getObject(); |
108 | if (!$this->supports($object)) { |
109 | return; |
110 | } |
111 | |
112 | $action = LogJournalActionDictionary::CREATE; |
113 | |
114 | switch (\get_class($args->getObject())) { |
115 | // l'ajout d'un participant ou d'une conformité entraine la modification de l'évaluation |
116 | case Conformite::class: |
117 | case Participant::class: |
118 | /** @var Conformite|Participant $object */ |
119 | $object = $object->getEvaluation(); |
120 | $action = LogJournalActionDictionary::UPDATE; |
121 | break; |
122 | // l'ajout d'un contact IL ou d'un service entraine la modification de la structure |
123 | case ComiteIlContact::class: |
124 | /** @var ComiteIlContact $object */ |
125 | $object = $object->getCollectivity(); |
126 | $action = LogJournalActionDictionary::UPDATE; |
127 | break; |
128 | // l'ajout d'une réponse entraine la modification de la conformité du traitement |
129 | case Reponse::class: |
130 | /** @var Reponse $object */ |
131 | $object = $object->getConformiteTraitement(); |
132 | $action = LogJournalActionDictionary::UPDATE; |
133 | break; |
134 | } |
135 | |
136 | $this->registerLog($object, $action); |
137 | } |
138 | |
139 | public function postUpdate(PostUpdateEventArgs $args): void |
140 | { |
141 | $object = $args->getObject(); |
142 | if (!($object instanceof LoggableSubject) || !$this->supports($object)) { |
143 | return; |
144 | } |
145 | |
146 | // specific case for user. Need to know which data is update |
147 | switch (get_class($object)) { |
148 | case User::class: |
149 | $this->registerLogForUser($object); |
150 | break; |
151 | case Proof::class: |
152 | case Request::class: |
153 | case Violation::class: |
154 | $this->registerLogSoftDelete($object); |
155 | break; |
156 | case Conformite::class: |
157 | case Participant::class: |
158 | $this->registerLog($object->getEvaluation(), LogJournalActionDictionary::UPDATE); |
159 | break; |
160 | case ComiteIlContact::class: |
161 | $this->registerLog($object->getCollectivity(), LogJournalActionDictionary::UPDATE); |
162 | break; |
163 | case Reponse::class: |
164 | /** @var User $user */ |
165 | $user = $this->security->getUser(); |
166 | $item = $this->cacheAdapter->getItem('already_register_item-' . $user->getId()->toString()); |
167 | $request = $this->requestStack->getCurrentRequest(); |
168 | $conformite = $object->getConformiteTraitement(); |
169 | $id = $request->get('id'); |
170 | // Cas spéciale lors de l'édition du traitement lors de la modification d'une conformité d'un traitement |
171 | if (\is_null($item->get()) || $id === $item->get()->toString() && $conformite->getId()->toString() === $id) { |
172 | $this->registerLog($conformite, LogJournalActionDictionary::UPDATE); |
173 | } |
174 | break; |
175 | default: |
176 | $this->registerLog($object, LogJournalActionDictionary::UPDATE); |
177 | } |
178 | } |
179 | |
180 | public function preRemove(PreRemoveEventArgs $args): void |
181 | { |
182 | if (!$this->supports($args->getObject())) { |
183 | return; |
184 | } |
185 | |
186 | $this->registerDeleteLog($args); |
187 | } |
188 | |
189 | private function registerLog(LoggableSubject $object, string $action) |
190 | { |
191 | /** @var User $user */ |
192 | $user = $this->security->getUser(); |
193 | $item = $this->cacheAdapter->getItem('already_register_item-' . $user->getId()->toString()); |
194 | // si la clef de l'objet existe déja alors on ajoute pas de log. |
195 | if (!$item->isHit()) { |
196 | $item->expiresAfter(2); |
197 | $item->set($object->getId()); |
198 | $this->cacheAdapter->save($item); |
199 | } elseif ($object->getId()->toString() === $item->get()->toString()) { |
200 | return; |
201 | } |
202 | |
203 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
204 | |
205 | $log = new LogJournal( |
206 | $this->getCollectivity($object), |
207 | $user->getFullName(), |
208 | $user->getEmail(), |
209 | $action, |
210 | $subject, |
211 | $object->getId()->toString(), |
212 | $object->__toString() |
213 | ); |
214 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
215 | } |
216 | |
217 | private function registerDeleteLog(PreRemoveEventArgs $args) |
218 | { |
219 | $object = $args->getObject(); |
220 | |
221 | if (!($object instanceof LoggableSubject) || $this->notConcernedByDeletionLog($object)) { |
222 | return; |
223 | } |
224 | |
225 | /** @var User $user */ |
226 | $user = $this->security->getUser(); |
227 | |
228 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
229 | |
230 | $log = new LogJournal( |
231 | $this->getCollectivity($object), |
232 | $user->getFullName(), |
233 | $user->getEmail(), |
234 | LogJournalActionDictionary::DELETE, |
235 | $subject, |
236 | $object->getId()->toString(), |
237 | $object->__toString() |
238 | ); |
239 | |
240 | $this->eventDispatcher->dispatch(new LogJournalEvent($log, $object)); |
241 | } |
242 | |
243 | private function supports($object): bool |
244 | { |
245 | return $object instanceof LoggableSubject && !\is_null($this->security->getUser()); |
246 | } |
247 | |
248 | private function getCollectivity($object) |
249 | { |
250 | if (Collectivity::class === \get_class($object)) { |
251 | return $object; |
252 | } |
253 | |
254 | if (\method_exists($object, 'getCollectivity')) { |
255 | return $object->getCollectivity(); |
256 | } |
257 | |
258 | /** @var User $user */ |
259 | $user = $this->security->getUser(); |
260 | |
261 | return $user->getCollectivity(); |
262 | } |
263 | |
264 | private function registerLogForUser(LoggableSubject $object) |
265 | { |
266 | /** @var User $user */ |
267 | $user = $this->security->getUser(); |
268 | $changes = $this->entityManager->getUnitOfWork()->getEntityChangeSet($object); |
269 | |
270 | // don't need to add log on lastLogin because already register in LoginSubscriber |
271 | if (\array_key_exists('lastlogin', $changes)) { |
272 | return; |
273 | } |
274 | |
275 | $collectivity = $this->getCollectivity($object); |
276 | if (\array_key_exists('deletedAt', $changes)) { |
277 | $action = LogJournalActionDictionary::SOFT_DELETE; |
278 | if (!\is_null($changes['deletedAt'][0])) { |
279 | $action = LogJournalActionDictionary::SOFT_DELETE_REVOKE; |
280 | } |
281 | $log = new LogJournal( |
282 | $collectivity, |
283 | $user->getFullName(), |
284 | $user->getEmail(), |
285 | $action, |
286 | LogJournalSubjectDictionary::USER_USER, |
287 | $object->getId()->toString(), |
288 | $object->__toString() |
289 | ); |
290 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
291 | |
292 | return; |
293 | } |
294 | |
295 | $subjectTypes = []; |
296 | if (\array_key_exists('firstName', $changes)) { |
297 | $subjectTypes[] = LogJournalSubjectDictionary::USER_FIRSTNAME; |
298 | } |
299 | |
300 | if (\array_key_exists('lastName', $changes)) { |
301 | $subjectTypes[] = LogJournalSubjectDictionary::USER_LASTNAME; |
302 | } |
303 | |
304 | if (\array_key_exists('email', $changes)) { |
305 | $subjectTypes[] = LogJournalSubjectDictionary::USER_EMAIL; |
306 | } |
307 | |
308 | if (\array_key_exists('password', $changes)) { |
309 | $subjectTypes[] = LogJournalSubjectDictionary::USER_PASSWORD; |
310 | } |
311 | |
312 | foreach ($subjectTypes as $subjectType) { |
313 | $log = new LogJournal( |
314 | $collectivity, |
315 | $user->getFullName(), |
316 | $user->getEmail(), |
317 | LogJournalActionDictionary::UPDATE, |
318 | $subjectType, |
319 | $object->getId()->toString(), |
320 | $object->__toString() |
321 | ); |
322 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
323 | } |
324 | } |
325 | |
326 | private function registerLogSoftDelete(LoggableSubject $object) |
327 | { |
328 | /** @var User $user */ |
329 | $user = $this->security->getUser(); |
330 | $changes = $this->entityManager->getUnitOfWork()->getEntityChangeSet($object); |
331 | $collectivity = $this->getCollectivity($object); |
332 | |
333 | // if is not a softdelete request, just register a log update |
334 | $action = LogJournalActionDictionary::UPDATE; |
335 | if (\array_key_exists('deletedAt', $changes)) { |
336 | $action = LogJournalActionDictionary::SOFT_DELETE; |
337 | } |
338 | |
339 | $subject = LogJournalSubjectDictionary::getSubjectFromClassName(\get_class($object)); |
340 | $log = new LogJournal( |
341 | $collectivity, |
342 | $user->getFullName(), |
343 | $user->getEmail(), |
344 | $action, |
345 | $subject, |
346 | $object->getId()->toString(), |
347 | $object->__toString() |
348 | ); |
349 | $this->eventDispatcher->dispatch(new LogJournalEvent($log)); |
350 | } |
351 | |
352 | private function notConcernedByDeletionLog(LoggableSubject $subject): bool |
353 | { |
354 | return \in_array(\get_class($subject), [ |
355 | Conformite::class, |
356 | Participant::class, |
357 | ComiteIlContact::class, |
358 | Service::class, |
359 | Reponse::class, |
360 | ]); |
361 | } |
362 | } |