Completed
Pull Request — develop (#302)
by Michiel
02:22
created

BootstrapGsspSecondFactorCommand::execute()   B

Complexity

Conditions 8
Paths 31

Size

Total Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 87
rs 7.0391
c 0
b 0
f 0
cc 8
nc 31
nop 2

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 2020 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 Rhumsaa\Uuid\Uuid;
23
use Surfnet\Stepup\Identity\Value\Institution;
24
use Surfnet\Stepup\Identity\Value\NameId;
25
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\UnverifiedSecondFactor;
26
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveGssfPossessionCommand;
27
use Symfony\Component\Console\Input\InputArgument;
28
use Symfony\Component\Console\Input\InputInterface;
29
use Symfony\Component\Console\Output\OutputInterface;
30
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
31
32
final class BootstrapGsspSecondFactorCommand extends AbstractBootstrapCommand
33
{
34
    protected function configure()
35
    {
36
        $this
37
            ->setDescription('Creates a Generic SAML Second Factor (GSSF) second factor for a specified user')
38
            ->addArgument('name-id', InputArgument::REQUIRED, 'The NameID of the identity to create')
39
            ->addArgument('institution', InputArgument::REQUIRED, 'The institution of the identity to create')
40
            ->addArgument(
41
                'gssp-token-type',
42
                InputArgument::REQUIRED,
43
                'The GSSP token type as defined in the GSSP config, for example tiqr or webauthn'
44
            )
45
            ->addArgument(
46
                'gssp-token-identifier',
47
                InputArgument::REQUIRED,
48
                'The identifier of the token as registered at the GSSP'
49
            )
50
            ->addArgument(
51
                'registration-status',
52
                InputArgument::REQUIRED,
53
                'Valid arguments: unverified, verified, vetted'
54
            )
55
            ->addArgument('actor-id', InputArgument::REQUIRED, 'The id of the vetting actor');
56
    }
57
58
    protected function execute(InputInterface $input, OutputInterface $output)
59
    {
60
        $this->tokenStorage->setToken(
61
            new AnonymousToken('cli.bootstrap-gssp-token', 'cli', ['ROLE_SS', 'ROLE_RA'])
0 ignored issues
show
Documentation introduced by
array('ROLE_SS', 'ROLE_RA') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a array<integer,object<Sym...curity\Core\Role\Role>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
62
        );
63
        $nameId = new NameId($input->getArgument('name-id'));
64
        $institutionText = $input->getArgument('institution');
65
        $institution = new Institution($institutionText);
0 ignored issues
show
Bug introduced by
It seems like $institutionText defined by $input->getArgument('institution') on line 64 can also be of type array<integer,string> or null; however, Surfnet\Stepup\Identity\...titution::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
66
        $mailVerificationRequired = $this->requiresMailVerification($institutionText);
67
        $registrationStatus = $input->getArgument('registration-status');
68
        $tokenType = $input->getArgument('gssp-token-type');
69
        $tokenIdentifier = $input->getArgument('gssp-token-identifier');
70
        $actorId = $input->getArgument('actor-id');
71
        $this->enrichEventMetadata($actorId);
72
        if (!$this->tokenBootstrapService->hasIdentityWithNameIdAndInstitution($nameId, $institution)) {
73
            $output->writeln(
74
                sprintf(
75
                    '<error>An identity with name ID "%s" from institution "%s" does not exist, create it first.</error>',
76
                    $nameId->getNameId(),
77
                    $institution->getInstitution()
78
                )
79
            );
80
81
            return;
82
        }
83
        $identity = $this->tokenBootstrapService->findOneByNameIdAndInstitution($nameId, $institution);
84
        $output->writeln(sprintf('<comment>Adding a %s %s GSSP token for %s</comment>', $registrationStatus, $tokenType, $identity->commonName));
85
        $this->beginTransaction();
86
        $secondFactorId = Uuid::uuid4()->toString();
87
88
        try {
89
            switch ($registrationStatus) {
90
                case "unverified":
91
                    $output->writeln(sprintf('<comment>Creating an unverified %s token</comment>', $tokenType));
92
                    $this->provePossession($secondFactorId, $identity, $tokenType, $tokenIdentifier);
93
                    break;
94
                case "verified":
95
                    $output->writeln(sprintf('<comment>Creating an unverified %s token</comment>', $tokenType));
96
                    $this->provePossession($secondFactorId, $identity, $tokenType, $tokenIdentifier);
97
                    $unverifiedSecondFactor = $this->tokenBootstrapService->findUnverifiedToken($identity->id, $tokenType);
98
                    if ($mailVerificationRequired) {
99
                        $output->writeln(sprintf('<comment>Creating an verified %s token</comment>', $tokenType));
100
                        $this->verifyEmail($identity, $unverifiedSecondFactor);
101
                    }
102
                    break;
103
                case "vetted":
104
                    $output->writeln(sprintf('<comment>Creating an unverified %s token</comment>', $tokenType));
105
                    $this->provePossession($secondFactorId, $identity, $tokenType, $tokenIdentifier);
106
                    /** @var UnverifiedSecondFactor $unverifiedSecondFactor */
107
                    $unverifiedSecondFactor = $this->tokenBootstrapService->findUnverifiedToken($identity->id, $tokenType);
108
                    if ($mailVerificationRequired) {
109
                        $output->writeln(sprintf('<comment>Creating an verified %s token</comment>', $tokenType));
110
                        $this->verifyEmail($identity, $unverifiedSecondFactor);
111
                    }
112
                    $verifiedSecondFactor = $this->tokenBootstrapService->findVerifiedToken($identity->id, $tokenType);
113
                    $output->writeln(sprintf('<comment>Vetting the verified %s token</comment>', $tokenType));
114
                    $this->vetSecondFactor(
115
                        $tokenType,
116
                        $actorId,
117
                        $identity,
118
                        $secondFactorId,
119
                        $verifiedSecondFactor,
120
                        $tokenIdentifier
121
                    );
122
                    break;
123
            }
124
            $this->finishTransaction();
125
        } catch (Exception $e) {
126
            $output->writeln(
127
                sprintf(
128
                    '<error>An Error occurred when trying to bootstrap the %s token: "%s"</error>',
129
                    $tokenType,
130
                    $e->getMessage()
131
                )
132
            );
133
            $this->rollback();
134
            throw $e;
135
        }
136
        $output->writeln(
137
            sprintf(
138
                '<info>Successfully %s %s second factor with UUID %s</info>',
139
                $registrationStatus,
140
                $tokenType,
141
                $secondFactorId
142
            )
143
        );
144
    }
145
146
    private function provePossession($secondFactorId, $identity, $tokenType, $tokenIdentifier)
147
    {
148
        $command = new ProveGssfPossessionCommand();
149
        $command->UUID = (string)Uuid::uuid4();
150
        $command->secondFactorId = $secondFactorId;
151
        $command->identityId = $identity->id;
152
        $command->stepupProvider = $tokenType;
153
        $command->gssfId = $tokenIdentifier;
154
        $this->process($command);
155
    }
156
}
157