Completed
Pull Request — master (#14)
by Tim
07:33
created

ConsentController::getconsent()   F

Complexity

Conditions 31
Paths 7814

Size

Total Lines 168
Code Lines 109

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 31
eloc 109
c 6
b 0
f 0
nc 7814
nop 1
dl 0
loc 168
rs 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
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\consent\Controller;
6
7
use Exception;
8
use SimpleSAML\Auth;
9
use SimpleSAML\Configuration;
10
use SimpleSAML\Error;
11
use SimpleSAML\HTTP\RunnableResponse;
12
use SimpleSAML\IdP;
13
use SimpleSAML\Locale\Translate;
14
use SimpleSAML\Logger;
15
use SimpleSAML\Module;
16
use SimpleSAML\Session;
17
use SimpleSAML\Stats;
18
use SimpleSAML\Utils;
19
use SimpleSAML\XHTML\Template;
20
use Symfony\Component\HttpFoundation\Request;
21
22
/**
23
 * Controller class for the consent module.
24
 *
25
 * This class serves the consent views available in the module.
26
 *
27
 * @package SimpleSAML\Module\consent
28
 */
29
class ConsentController
30
{
31
    /** @var \SimpleSAML\Configuration */
32
    protected $config;
33
34
    /** @var \SimpleSAML\Session */
35
    protected $session;
36
37
    /**
38
     * @var \SimpleSAML\Auth\State|string
39
     * @psalm-var \SimpleSAML\Auth\State|class-string
40
     */
41
    protected $authState = Auth\State::class;
42
43
    /**
44
     * @var \SimpleSAML\Logger|string
45
     * @psalm-var \SimpleSAML\Logger|class-string
46
     */
47
    protected $logger = Logger::class;
48
49
50
    /**
51
     * ConsentController constructor.
52
     *
53
     * @param \SimpleSAML\Configuration $config The configuration to use.
54
     * @param \SimpleSAML\Session $session The current user session.
55
     */
56
    public function __construct(Configuration $config, Session $session)
57
    {
58
        $this->config = $config;
59
        $this->session = $session;
60
    }
61
62
63
    /**
64
     * Inject the \SimpleSAML\Auth\State dependency.
65
     *
66
     * @param \SimpleSAML\Auth\State $authState
67
     */
68
    public function setAuthState(Auth\State $authState): void
69
    {
70
        $this->authState = $authState;
71
    }
72
73
74
    /**
75
     * Inject the \SimpleSAML\Logger dependency.
76
     *
77
     * @param \SimpleSAML\Logger $logger
78
     */
79
    public function setLogger(Logger $logger): void
80
    {
81
        $this->logger = $logger;
82
    }
83
84
85
    /**
86
     * Display consent form.
87
     *
88
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
89
     *
90
     * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse
91
     */
92
    public function getconsent(Request $request)
93
    {
94
//        session_cache_limiter('nocache');
95
96
        $this->logger::info('Consent - getconsent: Accessing consent interface');
97
98
        $stateId = $request->query->get('StateId');
99
        if ($stateId === null) {
100
            throw new Error\BadRequest('Missing required StateId query parameter.');
101
        }
102
103
        $state = $this->authState::loadState($stateId, 'consent:request');
104
105
        if (is_null($state)) {
106
            throw new Error\NoState();
107
        } elseif (array_key_exists('core:SP', $state)) {
108
            $spentityid = $state['core:SP'];
109
        } elseif (array_key_exists('saml:sp:State', $state)) {
110
            $spentityid = $state['saml:sp:State']['core:SP'];
111
        } else {
112
            $spentityid = 'UNKNOWN';
113
        }
114
115
        // The user has pressed the yes-button
116
        if ($request->query->get('yes') !== null) {
117
            if ($request->query->get('saveconsent') !== null) {
118
                $this->logger::stats('consentResponse remember');
119
            } else {
120
                $this->logger::stats('consentResponse rememberNot');
121
            }
122
123
            $statsInfo = [
124
                'remember' => $request->query->get('saveconsent'),
125
            ];
126
            if (isset($state['Destination']['entityid'])) {
127
                $statsInfo['spEntityID'] = $state['Destination']['entityid'];
128
            }
129
            Stats::log('consent:accept', $statsInfo);
130
131
            if (
132
                array_key_exists('consent:store', $state)
133
                && $request->query->get('saveconsent') === '1'
134
            ) {
135
                // Save consent
136
                $store = $state['consent:store'];
137
                $userId = $state['consent:store.userId'];
138
                $targetedId = $state['consent:store.destination'];
139
                $attributeSet = $state['consent:store.attributeSet'];
140
141
                $this->logger::debug(
142
                    'Consent - saveConsent() : [' . $userId . '|' . $targetedId . '|' . $attributeSet . ']'
143
                );
144
                try {
145
                    $store->saveConsent($userId, $targetedId, $attributeSet);
146
                } catch (Exception $e) {
147
                    $this->logger::error('Consent: Error writing to storage: ' . $e->getMessage());
148
                }
149
            }
150
151
            return new RunnableResponse([Auth\ProcessingChain::class, 'resumeProcessing'], [$state]);
152
        }
153
154
        // Prepare attributes for presentation
155
        $attributes = $state['Attributes'];
156
        $noconsentattributes = $state['consent:noconsentattributes'];
157
158
        // Remove attributes that do not require consent
159
        foreach ($attributes as $attrkey => $attrval) {
160
            if (in_array($attrkey, $noconsentattributes, true)) {
161
                unset($attributes[$attrkey]);
162
            }
163
        }
164
        $para = [
165
            'attributes' => &$attributes
166
        ];
167
168
        // Reorder attributes according to attributepresentation hooks
169
        Module::callHooks('attributepresentation', $para);
170
171
        // Parse parameters
172
        if (array_key_exists('name', $state['Source'])) {
173
            $srcName = $state['Source']['name'];
174
        } elseif (array_key_exists('OrganizationDisplayName', $state['Source'])) {
175
            $srcName = $state['Source']['OrganizationDisplayName'];
176
        } else {
177
            $srcName = $state['Source']['entityid'];
178
        }
179
180
        if (array_key_exists('name', $state['Destination'])) {
181
            $dstName = $state['Destination']['name'];
182
        } elseif (array_key_exists('OrganizationDisplayName', $state['Destination'])) {
183
            $dstName = $state['Destination']['OrganizationDisplayName'];
184
        } else {
185
            $dstName = $state['Destination']['entityid'];
186
        }
187
188
        // Make, populate and layout consent form
189
        $t = new Template($this->config, 'consent:consentform.twig');
190
        $translator = $t->getTranslator();
191
        $t->data['srcMetadata'] = $state['Source'];
192
        $t->data['dstMetadata'] = $state['Destination'];
193
        $t->data['yesTarget'] = Module::getModuleURL('consent/getconsent');
194
        $t->data['yesData'] = ['StateId' => $stateId];
195
        $t->data['noTarget'] = Module::getModuleURL('consent/noconsent');
196
        $t->data['noData'] = ['StateId' => $stateId];
197
        $t->data['attributes'] = $attributes;
198
        $t->data['checked'] = $state['consent:checked'];
199
        $t->data['stateId'] = $stateId;
200
201
        $t->data['srcName'] = htmlspecialchars(is_array($srcName) ? $translator->getPreferredTranslation($srcName) : $srcName);
202
        $t->data['dstName'] = htmlspecialchars(is_array($dstName) ? $translator->getPreferredTranslation($dstName) : $dstName);
203
204
        if (array_key_exists('descr_purpose', $state['Destination'])) {
205
            $t->data['dstDesc'] = $translator->getPreferredTranslation(
206
                Utils\Arrays::arrayize(
207
                    $state['Destination']['descr_purpose'],
208
                    'en'
209
                )
210
            );
211
        }
212
213
        // Fetch privacypolicy
214
        if (
215
            array_key_exists('UIInfo', $state['Destination']) &&
216
            array_key_exists('PrivacyStatementURL', $state['Destination']['UIInfo']) &&
217
            (!empty($state['Destination']['UIInfo']['PrivacyStatementURL']))
218
        ) {
219
            $privacypolicy = reset($state['Destination']['UIInfo']['PrivacyStatementURL']);
220
        } elseif (
221
            array_key_exists('UIInfo', $state['Source']) &&
222
            array_key_exists('PrivacyStatementURL', $state['Source']['UIInfo']) &&
223
            (!empty($state['Source']['UIInfo']['PrivacyStatementURL']))
224
        ) {
225
            $privacypolicy = reset($state['Source']['UIInfo']['PrivacyStatementURL']);
226
        } else {
227
            $privacypolicy = false;
228
        }
229
        if ($privacypolicy !== false) {
230
            $privacypolicy = str_replace(
231
                '%SPENTITYID%',
232
                urlencode($spentityid),
233
                $privacypolicy
234
            );
235
        }
236
        $t->data['sppp'] = $privacypolicy;
237
238
        // Set focus element
239
        switch ($state['consent:focus']) {
240
            case 'yes':
241
                $t->data['autofocus'] = 'yesbutton';
242
                break;
243
            case 'no':
244
                $t->data['autofocus'] = 'nobutton';
245
                break;
246
            case null:
247
            default:
248
                break;
249
        }
250
251
        $t->data['usestorage'] = array_key_exists('consent:store', $state);
252
253
        if (array_key_exists('consent:hiddenAttributes', $state)) {
254
            $t->data['hiddenAttributes'] = $state['consent:hiddenAttributes'];
255
        } else {
256
            $t->data['hiddenAttributes'] = [];
257
        }
258
259
        return $t;
260
    }
261
262
263
    /**
264
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
265
     *
266
     * @return \SimpleSAML\XHTML\Template
267
     */
268
    public function noconsent(Request $request): Template
269
    {
270
        $stateId = $request->query->get('StateId');
271
        if ($stateId === null) {
272
            throw new Error\BadRequest('Missing required StateId query parameter.');
273
        }
274
275
        $state = $this->authState::loadState($stateId, 'consent:request');
276
        if (is_null($state)) {
277
            throw new Error\NoState();
278
        }
279
280
        $resumeFrom = Module::getModuleURL(
281
            'consent/getconsent',
282
            ['StateId' => $stateId]
283
        );
284
285
        $logoutLink = Module::getModuleURL(
286
            'consent/logout',
287
            ['StateId' => $stateId]
288
        );
289
290
        $aboutService = null;
291
        if (!isset($state['consent:showNoConsentAboutService']) || $state['consent:showNoConsentAboutService']) {
292
            if (isset($state['Destination']['url.about'])) {
293
                $aboutService = $state['Destination']['url.about'];
294
            }
295
        }
296
297
        $statsInfo = [];
298
        if (isset($state['Destination']['entityid'])) {
299
            $statsInfo['spEntityID'] = $state['Destination']['entityid'];
300
        }
301
        Stats::log('consent:reject', $statsInfo);
302
303
        if (array_key_exists('name', $state['Destination'])) {
304
            $dstName = $state['Destination']['name'];
305
        } elseif (array_key_exists('OrganizationDisplayName', $state['Destination'])) {
306
            $dstName = $state['Destination']['OrganizationDisplayName'];
307
        } else {
308
            $dstName = $state['Destination']['entityid'];
309
        }
310
311
        $t = new Template($this->config, 'consent:noconsent.twig');
312
        $translator = $t->getTranslator();
313
        $t->data['dstMetadata'] = $state['Destination'];
314
        $t->data['resumeFrom'] = $resumeFrom;
315
        $t->data['aboutService'] = $aboutService;
316
        $t->data['logoutLink'] = $logoutLink;
317
        $t->data['dstName'] = htmlspecialchars(is_array($dstName) ? $translator->getPreferredTranslation($dstName) : $dstName);
318
        return $t;
319
    }
320
321
322
    /**
323
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
324
     *
325
     * @return \SimpleSAML\HTTP\RunnableResponse
326
     */
327
    public function logout(Request $request): RunnableResponse
328
    {
329
        $stateId = $request->query->get('StateId', null);
330
        if ($stateId === null) {
331
            throw new Error\BadRequest('Missing required StateId query parameter.');
332
        }
333
334
        $state = $this->authState::loadState($stateId, 'consent:request');
335
        if (is_null($state)) {
336
            throw new Error\NoState();
337
        }
338
        $state['Responder'] = ['\SimpleSAML\Module\consent\Logout', 'postLogout'];
339
340
        $idp = IdP::getByState($state);
341
        return new RunnableResponse([$idp, 'handleLogoutRequest'], [&$state, $stateId]);
342
    }
343
344
345
    /**
346
     * @return \SimpleSAML\XHTML\Template
347
     */
348
    public function logoutcompleted(): Template
349
    {
350
        return new Template($this->config, 'consent:logout_completed.twig');
351
    }
352
}
353