AuditLogProjector::applyAuditableEvent()   C
last analyzed

Complexity

Conditions 12
Paths 257

Size

Total Lines 63
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 35
nc 257
nop 2
dl 0
loc 63
rs 5.4208
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
0 ignored issues
show
Coding Style introduced by
Missing @link tag in file comment
Loading history...
18
19
namespace Surfnet\StepupMiddleware\ApiBundle\Identity\Projector;
20
21
use Broadway\Domain\DomainMessage;
22
use DateTime as CoreDateTime;
23
use Ramsey\Uuid\Uuid;
24
use Surfnet\Stepup\DateTime\DateTime;
25
use Surfnet\Stepup\Identity\AuditLog\Metadata;
26
use Surfnet\Stepup\Identity\Event\AuditableEvent;
27
use Surfnet\Stepup\Identity\Event\CompliedWithRecoveryCodeRevocationEvent;
28
use Surfnet\Stepup\Identity\Event\IdentityForgottenEvent;
29
use Surfnet\Stepup\Identity\Event\RecoveryTokenRevokedEvent;
30
use Surfnet\Stepup\Identity\Value\CommonName;
31
use Surfnet\Stepup\Identity\Value\Institution;
32
use Surfnet\Stepup\Identity\Value\RecoveryTokenIdentifierFactory;
33
use Surfnet\Stepup\Identity\Value\RecoveryTokenType;
34
use Surfnet\Stepup\Identity\Value\VettingType;
35
use Surfnet\Stepup\Projector\Projector;
36
use Surfnet\StepupMiddleware\ApiBundle\Exception\RuntimeException;
37
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\AuditLogEntry;
38
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\Identity;
39
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\AuditLogRepository;
40
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\IdentityRepository;
41
42
/**
43
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
44
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
45
class AuditLogProjector extends Projector
46
{
47
    public function __construct(
48
        private readonly AuditLogRepository $auditLogRepository,
49
        private readonly IdentityRepository $identityRepository,
50
    ) {
51
    }
52
53
    /**
54
     * @param DomainMessage $domainMessage
55
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
56
    public function handle(DomainMessage $domainMessage): void
57
    {
58
        $event = $domainMessage->getPayload();
59
60
        switch (true) {
61
            case $event instanceof IdentityForgottenEvent:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
62
                // Don't insert the IdentityForgottenEvent into the audit log, as we'd remove it immediately afterwards.
63
                $this->applyIdentityForgottenEvent($event);
64
                break;
65
            // Finally apply the auditable event, most events are auditable this so first handle the unique variants
66
            case $event instanceof AuditableEvent:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
67
                $this->applyAuditableEvent($event, $domainMessage);
68
                break;
69
        }
70
    }
71
72
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $event should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $domainMessage should have a doc-comment as per coding-style.
Loading history...
73
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
74
     * @SuppressWarnings(PHPMD.NPathComplexity)
75
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
76
    private function applyAuditableEvent(AuditableEvent $event, DomainMessage $domainMessage): void
0 ignored issues
show
Coding Style introduced by
Private method name "AuditLogProjector::applyAuditableEvent" must be prefixed with an underscore
Loading history...
77
    {
78
        $auditLogMetadata = $event->getAuditLogMetadata();
79
80
        $metadata = $domainMessage->getMetadata()->serialize();
81
        $entry = new AuditLogEntry();
82
        $entry->id = (string)Uuid::uuid4();
83
84
        if (isset($metadata['actorId'])) {
85
            $actor = $this->identityRepository->find($metadata['actorId']);
86
87
            if (!$actor instanceof Identity) {
88
                throw new RuntimeException(
89
                    sprintf(
90
                        'Cannot create AuditLogEntry, given Actor Identity "%s" does not exist',
91
                        $metadata['actorId'],
92
                    ),
93
                );
94
            }
95
96
            $entry->actorId = $metadata['actorId'];
97
            $entry->actorCommonName = $actor->commonName;
98
        }
99
100
        $this->augmentActorCommonName($entry, $auditLogMetadata);
101
102
        if (isset($metadata['actorInstitution'])) {
103
            $entry->actorInstitution = new Institution($metadata['actorInstitution']);
104
        }
105
106
        $entry->identityId = (string)$auditLogMetadata->identityId;
107
        $entry->identityInstitution = $auditLogMetadata->identityInstitution;
108
        $entry->event = $event::class;
109
        $entry->recordedOn = new DateTime(new CoreDateTime($domainMessage->getRecordedOn()->toString()));
110
111
        if ($auditLogMetadata->secondFactorId instanceof \Surfnet\Stepup\Identity\Value\SecondFactorId) {
112
            $entry->secondFactorId = (string)$auditLogMetadata->secondFactorId;
113
        }
114
115
        if ($auditLogMetadata->secondFactorType instanceof \Surfnet\StepupBundle\Value\SecondFactorType) {
116
            $entry->secondFactorType = (string)$auditLogMetadata->secondFactorType;
117
        }
118
119
        if (!$event instanceof RecoveryTokenRevokedEvent
120
            && !$event instanceof CompliedWithRecoveryCodeRevocationEvent
121
            && $auditLogMetadata->recoveryTokenId
122
        ) {
123
            $entry->recoveryTokenIdentifier = (string)$auditLogMetadata->recoveryTokenId;
124
        }
125
126
        if ($auditLogMetadata->recoveryTokenType instanceof \Surfnet\Stepup\Identity\Value\RecoveryTokenType) {
127
            $entry->recoveryTokenType = (string)$auditLogMetadata->recoveryTokenType;
128
        }
129
130
        if ($auditLogMetadata->secondFactorIdentifier instanceof \Surfnet\Stepup\Identity\Value\SecondFactorIdentifier) {
131
            $entry->secondFactorIdentifier = (string)$auditLogMetadata->secondFactorIdentifier;
132
        }
133
134
        if ($auditLogMetadata->raInstitution instanceof \Surfnet\Stepup\Identity\Value\Institution) {
135
            $entry->raInstitution = (string)$auditLogMetadata->raInstitution;
136
        }
137
138
        $this->auditLogRepository->save($entry);
139
    }
140
141
    protected function applyIdentityForgottenEvent(IdentityForgottenEvent $event): void
142
    {
143
        $entries = $this->auditLogRepository->findByIdentityId($event->identityId);
144
        foreach ($entries as $auditLogEntry) {
145
            $auditLogEntry->actorCommonName = CommonName::unknown();
146
147
            if ($auditLogEntry->recoveryTokenIdentifier) {
148
                $auditLogEntry->recoveryTokenIdentifier = RecoveryTokenIdentifierFactory::unknownForType(
149
                    new RecoveryTokenType($auditLogEntry->recoveryTokenType),
150
                );
151
            }
152
        }
153
154
        $entriesWhereActor = $this->auditLogRepository->findEntriesWhereIdentityIsActorOnly($event->identityId);
155
        foreach ($entriesWhereActor as $auditLogEntry) {
156
            $auditLogEntry->actorCommonName = CommonName::unknown();
157
        }
158
159
        $this->auditLogRepository->saveAll($entries);
160
        $this->auditLogRepository->saveAll($entriesWhereActor);
161
    }
162
163
    private function augmentActorCommonName(AuditLogEntry $entry, Metadata $auditLogMetadata): void
0 ignored issues
show
Coding Style introduced by
Private method name "AuditLogProjector::augmentActorCommonName" must be prefixed with an underscore
Loading history...
164
    {
165
        if (property_exists($auditLogMetadata, 'vettingType') && $auditLogMetadata->vettingType instanceof VettingType) {
166
            $entry->actorCommonName = new CommonName(
167
                $entry->actorCommonName->getCommonName() . $auditLogMetadata->vettingType->auditLog()
0 ignored issues
show
Bug introduced by
The method getCommonName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

167
                $entry->actorCommonName->/** @scrutinizer ignore-call */ 
168
                                         getCommonName() . $auditLogMetadata->vettingType->auditLog()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168
            );
169
        }
170
    }
171
}
172