Passed
Pull Request — master (#31)
by Tim
02:10
created

RegProcess::main()   C

Complexity

Conditions 12
Paths 48

Size

Total Lines 101
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 61
c 1
b 0
f 0
dl 0
loc 101
rs 6.4242
nc 48
nop 1
cc 12

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
namespace SimpleSAML\Module\webauthn\Controller;
4
5
use Exception;
6
use SimpleSAML\Auth;
7
use SimpleSAML\Configuration;
8
use SimpleSAML\Error;
9
use SimpleSAML\HTTP\RunnableResponse;
10
use SimpleSAML\Locale\Translate;
11
use SimpleSAML\Logger;
12
use SimpleSAML\Module;
13
use SimpleSAML\Module\webauthn\WebAuthn\AAGUID;
14
use SimpleSAML\Module\webauthn\WebAuthn\WebAuthnRegistrationEvent;
15
use SimpleSAML\Session;
16
use SimpleSAML\Utils;
17
use SimpleSAML\XHTML\Template;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\HttpFoundation\StreamedResponse;
22
23
/**
24
 * Controller class for the webauthn module.
25
 *
26
 * This class serves the different views available in the module.
27
 *
28
 * @package SimpleSAML\Module\webauthn
29
 */
30
class RegProcess
31
{
32
    /** @var \SimpleSAML\Configuration */
33
    protected $config;
34
35
    /** @var \SimpleSAML\Session */
36
    protected $session;
37
38
    /**
39
     * @var \SimpleSAML\Auth\State|string
40
     * @psalm-var \SimpleSAML\Auth\State|class-string
41
     */
42
    protected $authState = Auth\State::class;
43
44
    /**
45
     * @var \SimpleSAML\Logger|string
46
     * @psalm-var \SimpleSAML\Logger|class-string
47
     */
48
    protected $logger = Logger::class;
49
50
51
    /**
52
     * Controller constructor.
53
     *
54
     * It initializes the global configuration and session for the controllers implemented here.
55
     *
56
     * @param \SimpleSAML\Configuration              $config The configuration to use by the controllers.
57
     * @param \SimpleSAML\Session                    $session The session to use by the controllers.
58
     *
59
     * @throws \Exception
60
     */
61
    public function __construct(
62
        Configuration $config,
63
        Session $session
64
    ) {
65
        $this->config = $config;
66
        $this->session = $session;
67
    }
68
69
70
    /**
71
     * Inject the \SimpleSAML\Auth\State dependency.
72
     *
73
     * @param \SimpleSAML\Auth\State $authState
74
     */
75
    public function setAuthState(Auth\State $authState): void
76
    {
77
        $this->authState = $authState;
78
    }
79
80
81
    /**
82
     * Inject the \SimpleSAML\Logger dependency.
83
     *
84
     * @param \SimpleSAML\Logger $logger
85
     */
86
    public function setLogger(Logger $logger): void
87
    {
88
        $this->logger = $logger;
89
    }
90
91
92
    /**
93
     * @param \Symfony\Component\HttpFoundation\Request $request
94
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\SimpleSAML\HTTP\RunnableResponse|\Symfony\Component\HttpFoundation\StreamedResponse
95
     *   A Symfony Response-object.
96
     */
97
    public function main(Request $request): Response
98
    {
99
        if (session_status() != PHP_SESSION_ACTIVE) {
100
            session_cache_limiter('nocache');
101
        }
102
103
        $this->logger::info('FIDO2 - Accessing WebAuthn enrollment validation');
104
105
        $stateId = $request->query->get('StateId');
106
        if ($stateId === null) {
107
            throw new Error\BadRequest('Missing required StateId query parameter.');
108
        }
109
110
        $debugEnabled = $this->config->getValue('logging.level', Logger::NOTICE) === Logger::DEBUG;
111
112
        $state = $this->authState::loadState($stateId, 'webauthn:request');
113
114
        // registering a credential is only allowed for new users or after being authenticated
115
        if (count($state['FIDO2Tokens']) > 0 && $state['FIDO2AuthSuccessful'] === false) {
116
            throw new Exception("Attempt to register new token in unacceptable context.");
117
        }
118
119
        $fido2Scope = ($state['FIDO2Scope'] === null ? $state['FIDO2DerivedScope'] : $state['FIDO2Scope']);
120
        if ($fido2Scope === null) {
121
            throw new Exception("FIDO2Scope cannot be null!");
122
        }
123
124
        $regObject = new WebAuthnRegistrationEvent(
125
            $request->request->get('type'),
126
            $fido2Scope,
127
            $state['FIDO2SignupChallenge'],
128
            $state['IdPMetadata']['entityid'],
129
            base64_decode($_POST['attestation_object']),
130
            $request->request->get('response_id'),
131
            $request->request->get('attestation_client_data_json'),
132
            $debugEnabled
133
        );
134
135
        // at this point, we need to talk to the DB
136
        /**
137
         * STEP 19 of the validation procedure in § 7.1 of the spec: see if this credential is already registered
138
         */
139
        $store = $state['webauthn:store'];
140
        if ($store->doesCredentialExist(bin2hex($regObject->getCredentialId())) === false) {
141
            // credential does not exist yet in database, good.
142
        } else {
143
            throw new Exception("The credential with ID " . $regObject->getCredentialId() . " already exists.");
144
        }
145
146
        // THAT'S IT. This is a valid credential and can be enrolled to the user.
147
        $friendlyName = $request->request->get('tokenname');
148
149
        // if we have requested the token model, add it to the name
150
        if ($state['requestTokenModel']) {
151
            $model = Translate::noop('unknown model');
152
            $vendor = Translate::noop('unknown vendor');
153
            $aaguiddict = AAGUID::getInstance();
154
            if ($aaguiddict->hasToken($regObject->getAAGUID())) {
155
                $token = $aaguiddict->get($regObject->getAAGUID());
156
                $model = $token['model'];
157
                $vendor = $token['O'];
158
            }
159
            $friendlyName .= " ($model [$vendor])";
160
        }
161
162
        /**
163
         * STEP 20 of the validation procedure in § 7.1 of the spec: store credentialId, credential,
164
         * signCount and associate with user
165
         */
166
167
        $store->storeTokenData(
168
            $state['FIDO2Username'],
169
            $regObject->getCredentialId(),
170
            $regObject->getCredential(),
171
            $regObject->getCounter(),
172
            $friendlyName
173
        );
174
175
        // make sure $state gets the news, the token is to be displayed to the user on the next page
176
        $state['FIDO2Tokens'][] = [
177
            0 => $regObject->getCredentialId(),
178
            1 => $regObject->getCredential(),
179
            2 => $regObject->getCounter(),
180
            3 => $friendlyName
181
        ];
182
183
        $id = $this->authState::saveState($state, 'webauthn:request');
0 ignored issues
show
Bug introduced by
It seems like $state can also be of type null; however, parameter $state of SimpleSAML\Auth\State::saveState() does only seem to accept array, 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

183
        $id = $this->authState::saveState(/** @scrutinizer ignore-type */ $state, 'webauthn:request');
Loading history...
Unused Code introduced by
The assignment to $id is dead and can be removed.
Loading history...
184
        if ($debugEnabled === true) {
185
            $response = new StreamedResponse();
186
            $response->setCallback(function () {
187
                echo $regObject->getDebugBuffer();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $regObject seems to be never defined.
Loading history...
188
                echo $regObject->getValidateBuffer();
189
                echo "<form id='regform' method='POST' action='" .
190
                    Module::getModuleURL('webauthn/webauthn.php?StateId=' . urlencode($id)) . "'>";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id seems to be never defined.
Loading history...
191
                echo "<button type='submit'>Return to previous page.</button>";
192
            });
193
            return $response;
194
        } elseif (array_key_exists('Registration', $state)) {
195
            return new RedirectResponse(Module::getModuleURL('webauthn/webauthn.php?StateId=' . urlencode($stateId)));
196
        } else {
197
            return new RunnableResponse([Auth\ProcessingChain::class, 'resumeProcessing'], [$state]);
198
        }
199
    }
200
}
201