Passed
Push — main ( 7c62ee...93b57a )
by Johan
22:59 queued 17:41
created

MigrateSecondFactorCommand::__invoke()   B

Complexity

Conditions 6
Paths 34

Size

Total Lines 75
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 39
nc 34
nop 5
dl 0
loc 75
rs 8.6737
c 1
b 0
f 0

How to fix   Long Method   

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 2021 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
 */
18
19
namespace Surfnet\StepupMiddleware\MiddlewareBundle\Console\Command;
20
21
use Exception;
22
use InvalidArgumentException;
23
use Surfnet\Stepup\Identity\Value\Institution;
24
use Surfnet\Stepup\Identity\Value\NameId;
25
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\Identity;
26
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\VettedSecondFactor;
27
use Surfnet\StepupMiddleware\MiddlewareBundle\Service\BootstrapCommandService;
28
use Surfnet\StepupMiddleware\MiddlewareBundle\Service\TransactionHelper;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupMiddleware...rvice\TransactionHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use Symfony\Component\Console\Attribute\Argument;
30
use Symfony\Component\Console\Attribute\AsCommand;
31
use Symfony\Component\Console\Output\OutputInterface;
32
33
#[AsCommand(
34
    name: 'middleware:migrate:vetted-tokens',
35
    description: 'Migrates the tokens of an identity to a new institution while preserving the old tokens'
36
)]
37
final class MigrateSecondFactorCommand
38
{
39
    public function __construct(private readonly BootstrapCommandService $bootstrapService, private readonly TransactionHelper $transactionHelper)
40
    {
41
    }
42
43
    public function __invoke(
44
        #[Argument(description: 'The old NameID of the identity used as the source of the tokens to move', name: 'old-name-id')]
45
        string $oldNameId,
46
        #[Argument(description: 'The new NameID of the identity to move the tokens to', name: 'new-name-id')]
47
        string $newNameId,
48
        #[Argument(description: 'The institution of the target identity', name: 'target-institution')]
49
        ?string $targetInstitution,
50
        #[Argument(description: 'The e-mail address of the identity to create', name: 'email')]
51
        ?string $email,
52
        OutputInterface $output
53
    ): int {
54
        $sourceNameId = new NameId($oldNameId);
55
        $targetNameId = new NameId($newNameId);
56
57
        $output->writeln(sprintf('<comment>Starting token migration for %s</comment>', $sourceNameId));
58
        $sourceIdentity = $this->bootstrapService->getIdentityByNameId($sourceNameId);
59
60
        if ($sourceIdentity === null) {
61
            throw new InvalidArgumentException("oldNameId could net be resolved to a Identity.");
62
        }
63
64
        $targetIdentity = $this->bootstrapService->getIdentityByNameId($targetNameId);
65
66
        try {
67
            $this->transactionHelper->beginTransaction();
68
69
            // Check if target identity should be created
70
            if (!$targetIdentity instanceof Identity) {
71
                $output->writeln(
72
                    sprintf('<info>Target with NameID %s does not exist, creating new identity</info>', $targetNameId),
73
                );
74
75
                $identityId = $this->createIdentity($targetNameId, $sourceIdentity, $targetInstitution, $email);
76
77
                $output->writeln(
78
                    sprintf('<info>Successfully created identity with UUID %s</info>', $identityId),
79
                );
80
81
82
                $targetIdentity = $this->bootstrapService->getIdentityByNameId($targetNameId);
83
            }
84
85
            // Foreach token, perform the token move command
86
            $sourceVettedSecondFactors = $this->bootstrapService->getVettedSecondFactorsFromIdentity($sourceIdentity);
87
            $targetVettedSecondFactors = $this->bootstrapService->getVettedSecondFactorsFromIdentity($targetIdentity);
0 ignored issues
show
Bug introduced by
It seems like $targetIdentity can also be of type null; however, parameter $identity of Surfnet\StepupMiddleware...ndFactorsFromIdentity() does only seem to accept Surfnet\StepupMiddleware...dentity\Entity\Identity, maybe add an additional type check? ( Ignorable by Annotation )

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

87
            $targetVettedSecondFactors = $this->bootstrapService->getVettedSecondFactorsFromIdentity(/** @scrutinizer ignore-type */ $targetIdentity);
Loading history...
88
            foreach ($sourceVettedSecondFactors as $secondFactor) {
89
                if (!$this->tokenExists($targetVettedSecondFactors, $secondFactor)) {
90
                    $this->bootstrapService->migrateVettedSecondFactor($sourceIdentity, $targetIdentity, $secondFactor);
0 ignored issues
show
Bug introduced by
It seems like $targetIdentity can also be of type null; however, parameter $targetIdentity of Surfnet\StepupMiddleware...ateVettedSecondFactor() does only seem to accept Surfnet\StepupMiddleware...dentity\Entity\Identity, maybe add an additional type check? ( Ignorable by Annotation )

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

90
                    $this->bootstrapService->migrateVettedSecondFactor($sourceIdentity, /** @scrutinizer ignore-type */ $targetIdentity, $secondFactor);
Loading history...
91
                    $output->writeln(sprintf('<comment>Moved token %s</comment>', $secondFactor->id));
92
                } else {
93
                    $output->writeln(
94
                        sprintf('<info>Skipped moving token %s, already present"</info>', $secondFactor->id),
95
                    );
96
                }
97
            }
98
99
            $this->transactionHelper->finishTransaction();
100
        } catch (Exception $e) {
101
            $output->writeln(
102
                sprintf(
103
                    '<error>An Error occurred when trying to move the tokens of identity: "%s"</error>',
104
                    $e->getMessage(),
105
                ),
106
            );
107
            $this->transactionHelper->rollback();
108
            return 1;
109
        }
110
        $output->writeln(
111
            sprintf(
112
                '<info>Successfully moved tokens from identity %s to identity %s</info>',
113
                $sourceIdentity->id,
114
                $targetIdentity->id,
115
            ),
116
        );
117
        return 0;
118
    }
119
120
    /**
121
     * @return string
122
     */
123
    private function createIdentity(NameId $targetNameId, Identity $sourceIdentity, ?string $newInstitution, ?string $newEmail): string
124
    {
125
        if (!$newInstitution || !$newEmail) {
126
            throw new InvalidArgumentException("Missing email and institution");
127
        }
128
129
        $institution = new Institution($newInstitution);
130
131
        $identity = $this->bootstrapService->createIdentity(
132
            $institution,
133
            $targetNameId,
134
            $sourceIdentity->commonName->getCommonName(),
135
            $newEmail,
136
            $sourceIdentity->preferredLocale->getLocale(),
137
        );
138
139
        return $identity->id;
140
    }
141
142
    private function tokenExists(array $targetSecondFactors, VettedSecondFactor $sourceSecondFactor): bool
143
    {
144
        foreach ($targetSecondFactors as $secondFactor) {
145
            if ($secondFactor->isEqual($sourceSecondFactor)) {
146
                return true;
147
            }
148
        }
149
        return false;
150
    }
151
}
152