Passed
Pull Request — master (#14)
by Tim
02:44 queued 20s
created

ConsentController::getconsent()   F

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 166
Code Lines 109

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 31
eloc 109
c 4
b 0
f 0
nc 101089
nop 2
dl 0
loc 166
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
    /**
39
     * ConsentController constructor.
40
     *
41
     * @param \SimpleSAML\Configuration $config The configuration to use.
42
     * @param \SimpleSAML\Session $session The current user session.
43
     */
44
    public function __construct(Configuration $config, Session $session)
45
    {
46
        $this->config = $config;
47
        $this->session = $session;
48
    }
49
50
51
52
    /**
53
     * Display consent form.
54
     *
55
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
56
     * @param string $id The StateId
57
     *
58
     * @return \SimpleSAML\XHTML\Template
59
     */
60
    public function getconsent(Request $request, string $id): Template
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

60
    public function getconsent(/** @scrutinizer ignore-unused */ Request $request, string $id): Template

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
61
    {
62
        session_cache_limiter('nocache');
63
64
        Logger::info('Consent - getconsent: Accessing consent interface');
65
66
        $state = Auth\State::loadState($id, 'consent:request');
67
68
        if (is_null($state)) {
69
            throw new Error\NoState();
70
        } elseif (array_key_exists('core:SP', $state)) {
71
            $spentityid = $state['core:SP'];
72
        } elseif (array_key_exists('saml:sp:State', $state)) {
73
            $spentityid = $state['saml:sp:State']['core:SP'];
74
        } else {
75
            $spentityid = 'UNKNOWN';
76
        }
77
78
        // The user has pressed the yes-button
79
        if (array_key_exists('yes', $_REQUEST)) {
80
            if (array_key_exists('saveconsent', $_REQUEST)) {
81
                Logger::stats('consentResponse remember');
82
            } else {
83
                Logger::stats('consentResponse rememberNot');
84
            }
85
86
            $statsInfo = [
87
                'remember' => array_key_exists('saveconsent', $_REQUEST),
88
            ];
89
            if (isset($state['Destination']['entityid'])) {
90
                $statsInfo['spEntityID'] = $state['Destination']['entityid'];
91
            }
92
            Stats::log('consent:accept', $statsInfo);
93
94
            if (
95
                array_key_exists('consent:store', $state)
96
                && array_key_exists('saveconsent', $_REQUEST)
97
                && $_REQUEST['saveconsent'] === '1'
98
            ) {
99
                // Save consent
100
                $store = $state['consent:store'];
101
                $userId = $state['consent:store.userId'];
102
                $targetedId = $state['consent:store.destination'];
103
                $attributeSet = $state['consent:store.attributeSet'];
104
105
                Logger::debug(
106
                    'Consent - saveConsent() : [' . $userId . '|' . $targetedId . '|' . $attributeSet . ']'
107
                );
108
                try {
109
                    $store->saveConsent($userId, $targetedId, $attributeSet);
110
                } catch (Exception $e) {
111
                    Logger::error('Consent: Error writing to storage: ' . $e->getMessage());
112
                }
113
            }
114
115
            Auth\ProcessingChain::resumeProcessing($state);
116
        }
117
118
        // Prepare attributes for presentation
119
        $attributes = $state['Attributes'];
120
        $noconsentattributes = $state['consent:noconsentattributes'];
121
122
        // Remove attributes that do not require consent
123
        foreach ($attributes as $attrkey => $attrval) {
124
            if (in_array($attrkey, $noconsentattributes, true)) {
125
                unset($attributes[$attrkey]);
126
            }
127
        }
128
        $para = [
129
            'attributes' => &$attributes
130
        ];
131
132
        // Reorder attributes according to attributepresentation hooks
133
        Module::callHooks('attributepresentation', $para);
134
135
        // Parse parameters
136
        if (array_key_exists('name', $state['Source'])) {
137
            $srcName = $state['Source']['name'];
138
        } elseif (array_key_exists('OrganizationDisplayName', $state['Source'])) {
139
            $srcName = $state['Source']['OrganizationDisplayName'];
140
        } else {
141
            $srcName = $state['Source']['entityid'];
142
        }
143
144
        if (array_key_exists('name', $state['Destination'])) {
145
            $dstName = $state['Destination']['name'];
146
        } elseif (array_key_exists('OrganizationDisplayName', $state['Destination'])) {
147
            $dstName = $state['Destination']['OrganizationDisplayName'];
148
        } else {
149
            $dstName = $state['Destination']['entityid'];
150
        }
151
152
        // Make, populate and layout consent form
153
        $t = new Template($this->config, 'consent:consentform.twig');
154
        $translator = $t->getTranslator();
155
        $t->data['srcMetadata'] = $state['Source'];
156
        $t->data['dstMetadata'] = $state['Destination'];
157
        $t->data['yesTarget'] = Module::getModuleURL('consent/getconsent');
158
        $t->data['yesData'] = ['StateId' => $id];
159
        $t->data['noTarget'] = Module::getModuleURL('consent/noconsent');
160
        $t->data['noData'] = ['StateId' => $id];
161
        $t->data['attributes'] = $attributes;
162
        $t->data['checked'] = $state['consent:checked'];
163
        $t->data['stateId'] = $id;
164
165
        $t->data['srcName'] = htmlspecialchars(is_array($srcName) ? $translator->getPreferredTranslation($srcName) : $srcName);
166
        $t->data['dstName'] = htmlspecialchars(is_array($dstName) ? $translator->getPreferredTranslation($dstName) : $dstName);
167
168
        if (array_key_exists('descr_purpose', $state['Destination'])) {
169
            $t->data['dstDesc'] = $translator->getPreferredTranslation(
170
                Utils\Arrays::arrayize(
171
                    $state['Destination']['descr_purpose'],
172
                    'en'
173
                )
174
            );
175
        }
176
177
        // Fetch privacypolicy
178
        if (
179
            array_key_exists('UIInfo', $state['Destination']) &&
180
            array_key_exists('PrivacyStatementURL', $state['Destination']['UIInfo']) &&
181
            (!empty($state['Destination']['UIInfo']['PrivacyStatementURL']))
182
        ) {
183
            $privacypolicy = reset($state['Destination']['UIInfo']['PrivacyStatementURL']);
184
        } elseif (
185
            array_key_exists('UIInfo', $state['Source']) &&
186
            array_key_exists('PrivacyStatementURL', $state['Source']['UIInfo']) &&
187
            (!empty($state['Source']['UIInfo']['PrivacyStatementURL']))
188
        ) {
189
            $privacypolicy = reset($state['Source']['UIInfo']['PrivacyStatementURL']);
190
        } else {
191
            $privacypolicy = false;
192
        }
193
        if ($privacypolicy !== false) {
194
            $privacypolicy = str_replace(
195
                '%SPENTITYID%',
196
                urlencode($spentityid),
197
                $privacypolicy
198
            );
199
        }
200
        $t->data['sppp'] = $privacypolicy;
201
202
        // Set focus element
203
        switch ($state['consent:focus']) {
204
            case 'yes':
205
                $t->data['autofocus'] = 'yesbutton';
206
                break;
207
            case 'no':
208
                $t->data['autofocus'] = 'nobutton';
209
                break;
210
            case null:
211
            default:
212
                break;
213
        }
214
215
        $t->data['usestorage'] = array_key_exists('consent:store', $state);
216
217
        if (array_key_exists('consent:hiddenAttributes', $state)) {
218
            $t->data['hiddenAttributes'] = $state['consent:hiddenAttributes'];
219
        } else {
220
            $t->data['hiddenAttributes'] = [];
221
        }
222
223
        $t->data['attributes_html'] = $this->presentAttributes($t, $attributes, '');
224
225
        return $t;
226
    }
227
228
229
    /**
230
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
231
     * @param string $id The StateId
232
     *
233
     * @return \SimpleSAML\XHTML\Template
234
     */
235
    public function noconsent(Request $request, string $id): Template
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

235
    public function noconsent(/** @scrutinizer ignore-unused */ Request $request, string $id): Template

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
236
    {
237
        /** @psalm-var array $state */
238
        $state = Auth\State::loadState($id, 'consent:request');
239
240
        $resumeFrom = Module::getModuleURL(
241
            'consent/getconsent',
242
            ['StateId' => $id]
243
        );
244
245
        $logoutLink = Module::getModuleURL(
246
            'consent/logout',
247
            ['StateId' => $id]
248
        );
249
250
        $aboutService = null;
251
        if (!isset($state['consent:showNoConsentAboutService']) || $state['consent:showNoConsentAboutService']) {
252
            if (isset($state['Destination']['url.about'])) {
253
                $aboutService = $state['Destination']['url.about'];
254
            }
255
        }
256
257
        $statsInfo = [];
258
        if (isset($state['Destination']['entityid'])) {
259
            $statsInfo['spEntityID'] = $state['Destination']['entityid'];
260
        }
261
        Stats::log('consent:reject', $statsInfo);
262
263
        if (array_key_exists('name', $state['Destination'])) {
264
            $dstName = $state['Destination']['name'];
265
        } elseif (array_key_exists('OrganizationDisplayName', $state['Destination'])) {
266
            $dstName = $state['Destination']['OrganizationDisplayName'];
267
        } else {
268
            $dstName = $state['Destination']['entityid'];
269
        }
270
271
        $t = new Template($this->config, 'consent:noconsent.twig');
272
        $translator = $t->getTranslator();
273
        $t->data['dstMetadata'] = $state['Destination'];
274
        $t->data['resumeFrom'] = $resumeFrom;
275
        $t->data['aboutService'] = $aboutService;
276
        $t->data['logoutLink'] = $logoutLink;
277
        $t->data['dstName'] = htmlspecialchars(is_array($dstName) ? $translator->getPreferredTranslation($dstName) : $dstName);
278
        return $t;
279
    }
280
281
282
    /**
283
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
284
     * @param string $id The StateId
285
     *
286
     * @return \SimpleSAML\HTTP\RunnableResponse
287
     */
288
    public function logout(Request $request, string $id): RunnableResponse
289
    {
290
        $state = Auth\State::loadState($id, 'consent:request');
291
292
        $state['Responder'] = ['\SimpleSAML\Module\consent\Logout', 'postLogout'];
293
294
        $idp = IdP::getByState($state);
0 ignored issues
show
Bug introduced by
It seems like $state can also be of type null; however, parameter $state of SimpleSAML\IdP::getByState() 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

294
        $idp = IdP::getByState(/** @scrutinizer ignore-type */ $state);
Loading history...
295
        return new RunnableResponse([$idp, 'handleLogoutRequest'], [$state, null]);
296
    }
297
298
299
    /**
300
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
301
     *
302
     * @return \SimpleSAML\XHTML\Template
303
     */
304
    public function logoutcompleted(Request $request): Template
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

304
    public function logoutcompleted(/** @scrutinizer ignore-unused */ Request $request): Template

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
305
    {
306
        return new Template($this->config, 'consent:logout_completed.twig');
307
    }
308
309
310
    /**
311
     * Recursive attribute array listing function
312
     *
313
     * @param \SimpleSAML\XHTML\Template $t          Template object
314
     * @param array                     $attributes Attributes to be presented
315
     * @param string                    $nameParent Name of parent element
316
     *
317
     * @return string HTML representation of the attributes
318
     */
319
    private function presentAttributes(Template $t, array $attributes, string $nameParent): string
320
    {
321
        $translator = $t->getTranslator();
322
323
        $alternate = ['odd', 'even'];
324
        $i = 0;
325
        $summary = 'summary="' . $translator->t('List the information about you that is about to be transmitted to the service you are going to login to') . '"';
0 ignored issues
show
Bug introduced by
The method t() does not exist on SimpleSAML\Locale\Translate. ( Ignorable by Annotation )

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

325
        $summary = 'summary="' . $translator->/** @scrutinizer ignore-call */ t('List the information about you that is about to be transmitted to the service you are going to login to') . '"';

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
326
327
        if (strlen($nameParent) > 0) {
328
            $parentStr = strtolower($nameParent) . '_';
329
            $str = '<table class="attributes" ' . $summary . '>';
330
        } else {
331
            $parentStr = '';
332
            $str = '<table id="table_with_attributes" class="attributes" ' . $summary . '>';
333
            $str .= "\n" . '<caption>' . $translator->t('User information') . '</caption>';
334
        }
335
336
        foreach ($attributes as $name => $value) {
337
            $nameraw = $name;
338
            $name = $translator->getAttributeTranslation($parentStr . $nameraw);
339
340
            if (preg_match('/^child_/', $nameraw)) {
341
                // insert child table
342
                $parentName = preg_replace('/^child_/', '', $nameraw);
343
                foreach ($value as $child) {
344
                    $str .= "\n" . '<tr class="odd"><td class="td_odd">' .
345
                        $this->presentAttributes($t, $child, $parentName) . '</td></tr>';
346
                }
347
            } else {
348
                // insert values directly
349
350
                $str .= "\n" . '<tr class="' . $alternate[($i++ % 2)] .
351
                    '"><td><span class="attrname">' . htmlspecialchars($name) . '</span></td>';
352
353
                $isHidden = in_array($nameraw, $t->data['hiddenAttributes'], true);
354
                if ($isHidden) {
355
                    $hiddenId = Utils\Random::generateID();
356
                    $str .= '<td><span class="attrvalue hidden" id="hidden_' . $hiddenId . '">';
357
                } else {
358
                    $hiddenId = '';
359
                    $str .= '<td><span class="attrvalue">';
360
                }
361
362
                if (sizeof($value) > 1) {
363
                    // we hawe several values
364
                    $str .= '<ul>';
365
                    foreach ($value as $listitem) {
366
                        if ($nameraw === 'jpegPhoto') {
367
                            $str .= '<li><img src="data:image/jpeg;base64,' .
368
                                htmlspecialchars($listitem) . '" alt="User photo" /></li>';
369
                        } else {
370
                            $str .= '<li>' . htmlspecialchars($listitem) . '</li>';
371
                        }
372
                    }
373
                    $str .= '</ul>';
374
                } elseif (isset($value[0])) {
375
                    // we hawe only one value
376
                    if ($nameraw === 'jpegPhoto') {
377
                        $str .= '<img src="data:image/jpeg;base64,' .
378
                            htmlspecialchars($value[0]) . '" alt="User photo" />';
379
                    } else {
380
                        $str .= htmlspecialchars($value[0]);
381
                    }
382
                } // end of if multivalue
383
                $str .= '</span>';
384
385
                if ($isHidden) {
386
                    $str .= '<div class="attrvalue consent_showattribute" id="visible_' . $hiddenId . '">';
387
                    $str .= '... ';
388
                    $str .= '<a class="consent_showattributelink" href="javascript:SimpleSAML_show(\'hidden_' . $hiddenId;
389
                    $str .= '\'); SimpleSAML_hide(\'visible_' . $hiddenId . '\');">';
390
                    $str .= $translator->t('Show attributes');
391
                    $str .= '</a>';
392
                    $str .= '</div>';
393
                }
394
395
                $str .= '</td></tr>';
396
            }       // end else: not child table
397
        }   // end foreach
398
        $str .= '</table>';
399
        return $str;
400
    }
401
}
402