Passed
Push — master ( 874950...a25f1e )
by Stefan
02:55 queued 13s
created

WebAuthn::main()   F

Complexity

Conditions 12
Paths 385

Size

Total Lines 78
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 12
eloc 48
c 4
b 1
f 0
nc 385
nop 1
dl 0
loc 78
rs 3.8208

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 SimpleSAML\Auth;
6
use SimpleSAML\Configuration;
7
use SimpleSAML\Error;
8
use SimpleSAML\Logger;
9
use SimpleSAML\Module;
10
use SimpleSAML\Session;
11
use SimpleSAML\Utils;
12
use SimpleSAML\XHTML\Template;
13
use Symfony\Component\HttpFoundation\Request;
14
15
/**
16
 * Controller class for the webauthn module.
17
 *
18
 * This class serves the different views available in the module.
19
 *
20
 * @package SimpleSAML\Module\webauthn
21
 */
22
class WebAuthn
23
{
24
    /** @var \SimpleSAML\Configuration */
25
    protected Configuration $config;
26
27
    /** @var \SimpleSAML\Session */
28
    protected Session $session;
29
30
    /**
31
     * @var \SimpleSAML\Auth\State|string
32
     * @psalm-var \SimpleSAML\Auth\State|class-string
33
     */
34
    protected $authState = Auth\State::class;
35
36
    /**
37
     * @var \SimpleSAML\Logger|string
38
     * @psalm-var \SimpleSAML\Logger|class-string
39
     */
40
    protected $logger = Logger::class;
41
42
43
    /**
44
     * Controller constructor.
45
     *
46
     * It initializes the global configuration and session for the controllers implemented here.
47
     *
48
     * @param \SimpleSAML\Configuration              $config The configuration to use by the controllers.
49
     * @param \SimpleSAML\Session                    $session The session to use by the controllers.
50
     *
51
     * @throws \Exception
52
     */
53
    public function __construct(
54
        Configuration $config,
55
        Session $session
56
    ) {
57
        $this->config = $config;
58
        $this->session = $session;
59
    }
60
61
62
    /**
63
     * Inject the \SimpleSAML\Auth\State dependency.
64
     *
65
     * @param \SimpleSAML\Auth\State $authState
66
     */
67
    public function setAuthState(Auth\State $authState): void
68
    {
69
        $this->authState = $authState;
70
    }
71
72
73
    /**
74
     * Inject the \SimpleSAML\Logger dependency.
75
     *
76
     * @param \SimpleSAML\Logger $logger
77
     */
78
    public function setLogger(Logger $logger): void
79
    {
80
        $this->logger = $logger;
81
    }
82
83
    const STATE_AUTH_NOMGMT = 1; // just authenticate user
84
    const STATE_AUTH_ALLOWMGMT = 2; // allow to switch to mgmt page
85
    const STATE_MGMT = 4; // show token management page
86
87
88
    public static function workflowStateMachine($state) {
89
        // if we don't have any credentials yet, allow user to register
90
        // regardless if in inflow or standalone (redirect to standalone if need
91
        // be)
92
        if (!isset($state['FIDO2Tokens']) || count($state['FIDO2Tokens']) == 0) {
93
            return self::STATE_MGMT;
94
        }
95
        // from here on we do have a credential to work with
96
        //
97
        // user indicated he wants to manage tokens. He did so either by
98
        // visiting the Registration page, or by checking the box during
99
        // inflow.
100
        // If coming from inflow, allow management only if user is
101
        // properly authenticated, otherwise send to auth page
102
        if ($state['FIDO2WantsRegister']) {
103
            if ($state['FIDO2AuthSuccessful'] || $state['Registration']) {
104
                return self::STATE_MGMT;
105
            }
106
            return self::STATE_AUTH_ALLOWMGMT;
107
        } else { // in inflow, allow to check the management box; otherwise,
108
                 // only auth
109
            return $state['UseInflowRegistration'] ? self::STATE_AUTH_ALLOWMGMT : self::STATE_AUTH_NOMGMT;
110
        }
111
    }
112
113
    /**
114
     * @param \Symfony\Component\HttpFoundation\Request $request
115
     * @return \SimpleSAML\XHTML\Template  A Symfony Response-object.
116
     */
117
    public function main(Request $request): Template
118
    {
119
        $this->logger::info('FIDO2 - Accessing WebAuthn interface');
120
121
        $stateId = $request->query->get('StateId');
122
        if ($stateId === null) {
123
            throw new Error\BadRequest('Missing required StateId query parameter.');
124
        }
125
126
        $state = $this->authState::loadState($stateId, 'webauthn:request');
127
128
        if ( $this->workflowStateMachine($state) != self::STATE_AUTH_NOMGMT ) {
129
            $templateFile = 'webauthn:webauthn.twig'; 
130
        } else {
131
            $templateFile = 'webauthn:authentication.twig';
132
        }
133
134
        // Make, populate and layout consent form
135
        $t = new Template($this->config, $templateFile);
136
        $t->data['UserID'] = $state['FIDO2Username'];
137
        $t->data['FIDO2Tokens'] = $state['FIDO2Tokens'];
138
139
        $challenge = str_split($state['FIDO2SignupChallenge'], 2);
140
        $entityid = $state['Source']['entityid'];
141
        $configUtils = new Utils\Config();
142
        $username = str_split(
143
            hash('sha512', $state['FIDO2Username'] . '|' . $configUtils->getSecretSalt() . '|' . $entityid),
144
            2
145
        );
146
147
        $challengeEncoded = [];
148
        foreach ($challenge as $oneChar) {
149
            $challengeEncoded[] = hexdec($oneChar);
150
        }
151
152
        $credentialIdEncoded = [];
153
        foreach ($state['FIDO2Tokens'] as $number => $token) {
154
            $idSplit = str_split($token[0], 2);
155
            $credentialIdEncoded[$number] = [];
156
            foreach ($idSplit as $credIdBlock) {
157
                $credentialIdEncoded[$number][] = hexdec($credIdBlock);
158
            }
159
        }
160
161
        $usernameEncoded = [];
162
        foreach ($username as $oneChar) {
163
            $usernameEncoded[] = hexdec($oneChar);
164
        }
165
166
        $frontendData = [];
167
        $frontendData['challengeEncoded'] = $challengeEncoded;
168
        $frontendData['state'] = [];
169
        foreach (['Source', 'FIDO2Scope','FIDO2Username','FIDO2Displayname','requestTokenModel'] as $stateItem) {
170
            $frontendData['state'][$stateItem] = $state[$stateItem];
171
        }
172
173
        $t->data['showExitButton'] = !array_key_exists('Registration', $state);
174
        $frontendData['usernameEncoded'] = $usernameEncoded;
175
        $frontendData['attestation'] = $state['requestTokenModel'] ? "indirect" : "none";
176
        $frontendData['credentialIdEncoded'] = $credentialIdEncoded;
177
        $t->data['frontendData'] = json_encode($frontendData);
178
179
        $t->data['FIDO2AuthSuccessful'] = $state['FIDO2AuthSuccessful'];
180
        if ( $this->workflowStateMachine($state) == self::STATE_MGMT ) {
181
            $t->data['regURL'] = Module::getModuleURL('webauthn/regprocess?StateId=' . urlencode($stateId));
182
            $t->data['delURL'] = Module::getModuleURL('webauthn/managetoken?StateId=' . urlencode($stateId));
183
184
        }
185
186
        $t->data['authForm'] = "";
187
        if (
188
            $this->workflowStateMachine($state) == self::STATE_AUTH_ALLOWMGMT || $this->workflowStateMachine($state) == self::STATE_AUTH_NOMGMT
189
        ) {
190
            $t->data['authURL'] = Module::getModuleURL('webauthn/authprocess?StateId=' . urlencode($stateId));
191
        }
192
193
        // dynamically generate the JS code needed for token registration
194
        return $t;
195
    }
196
}
197