Passed
Pull Request — master (#3)
by Tim
02:40
created

ConsentAdmin::driveProcessingChain()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 63
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 23
c 1
b 0
f 0
nc 6
nop 8
dl 0
loc 63
rs 9.552

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\consentadmin\Controller;
6
7
use Exception;
8
use SimpleSAML\Auth;
9
use SimpleSAML\Configuration;
10
use SimpleSAML\Logger;
11
use SimpleSAML\Metadata\MetaDataStorageHandler;
12
use SimpleSAML\Module\consent\Auth\Process\Consent;
13
use SimpleSAML\Module\consent\Store;
14
use SimpleSAML\Session;
15
use SimpleSAML\XHTML\Template;
16
use Symfony\Component\HttpFoundation\Request;
17
18
/**
19
 * Controller class for the consentadmin module.
20
 *
21
 * This class serves the different views available in the module.
22
 *
23
 * @package simplesamlphp/simplesamlphp-module-consentadmin
24
 */
25
class ConsentAdmin
26
{
27
    /** @var \SimpleSAML\Configuration */
28
    protected Configuration $config;
29
30
    /** @var \SimpleSAML\Configuration */
31
    protected Configuration $moduleConfig;
32
33
    /** @var \SimpleSAML\Session */
34
    protected Session $session;
35
36
    /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */
37
    protected MetaDataStorageHandler $metadataStorageHandler;
38
39
    /**
40
     * @var \SimpleSAML\Auth\Simple|string
41
     * @psalm-var \SimpleSAML\Auth\Simple|class-string
42
     */
43
    protected $authSimple = Auth\Simple::class;
44
45
    /**
46
     * @var \SimpleSAML\Module\consent\Auth\Process\Consent|string
47
     * @psalm-var \SimpleSAML\Module\consent\Auth\Process\Consent|class-string
48
     */
49
    protected $consent = Consent::class;
50
51
    /**
52
     * @var \SimpleSAML\Module\consent\Store|string
53
     * @psalm-var \SimpleSAML\Module\consent\Store|class-string
54
     */
55
    protected $store = Store::class;
56
57
58
    /**
59
     * Controller constructor.
60
     *
61
     * It initializes the global configuration and session for the controllers implemented here.
62
     *
63
     * @param \SimpleSAML\Configuration $config The configuration to use by the controllers.
64
     * @param \SimpleSAML\Session $session The session to use by the controllers.
65
     *
66
     * @throws \Exception
67
     */
68
    public function __construct(
69
        Configuration $config,
70
        Session $session
71
    ) {
72
        $this->config = $config;
73
        $this->moduleConfig = Configuration::getConfig('module_consentAdmin.php');
74
        $this->metadataStorageHandler = MetaDataStorageHandler::getMetadataHandler();
75
        $this->session = $session;
76
    }
77
78
79
    /**
80
     * Inject the \SimpleSAML\Module\consent\Store dependency.
81
     *
82
     * @param \SimpleSAML\Module\consent\Store $store
83
     */
84
    public function setStore(Store $store): void
85
    {
86
        $this->store = $store;
87
    }
88
89
90
    /**
91
     * Inject the \SimpleSAML\Auth\Simple dependency.
92
     *
93
     * @param \SimpleSAML\Auth\Simple $authSimple
94
     */
95
    public function setAuthSimple(Auth\Simple $authSimple): void
96
    {
97
        $this->authSimple = $authSimple;
98
    }
99
100
101
    /**
102
     * Inject the \SimpleSAML\Metadata\MetaDataStorageHandler dependency.
103
     *
104
     * @param \SimpleSAML\Metadata\MetaDataStorageHandler $handler
105
     */
106
    public function setMetadataStorageHandler(MetadataStorageHandler $handler): void
107
    {
108
        $this->metadataStorageHandler = $handler;
109
    }
110
111
112
    /**
113
     * Inject the \SimpleSAML\Module\consent\Auth\Process\Consent dependency.
114
     *
115
     * @param \SimpleSAML\Module\consent\Auth\Process\Consent $consent
116
     */
117
    public function setConsent(Consent $consent): void
118
    {
119
        $this->consent = $consent;
120
    }
121
122
123
    /**
124
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
125
     *
126
     * @return \SimpleSAML\XHTML\Template
127
     */
128
    public function main(Request $request): Template
129
    {
130
        $authority = $this->moduleConfig->getValue('authority');
131
132
        $as = new $this->authSimple($authority);
133
134
        // If request is a logout request
135
        $logout = $request->get('logout');
136
        if ($logout !== null) {
137
            $returnURL = $this->moduleConfig->getValue('returnURL');
138
            $as->logout($returnURL);
139
        }
140
141
        $hashAttributes = $this->moduleConfig->getValue('attributes.hash', false);
142
143
        $excludeAttributes = $this->moduleConfig->getValue('attributes.exclude', []);
144
145
        // Check if valid local session exists
146
        $as->requireAuth();
147
148
        // Get released attributes
149
        $attributes = $as->getAttributes();
150
151
        // Get metadata storage handler
152
        $metadata = $this->metadataStorageHandler;
153
154
        /*
155
         * Get IdP id and metadata
156
         */
157
        $idp_entityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
158
        $idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-hosted');
159
160
        // Calc correct source
161
        if ($as->getAuthData('saml:sp:IdP') !== null) {
162
            // from a remote idp (as bridge)
163
            $source = 'saml20-idp-remote|' . $as->getAuthData('saml:sp:IdP');
164
        } else {
165
            // from the local idp
166
            $source = $idp_metadata['metadata-set'] . '|' . $idp_entityid;
167
        }
168
169
        // Get user ID
170
        if (isset($idp_metadata['userid.attribute']) && is_string($idp_metadata['userid.attribute'])) {
171
            $userid_attributename = $idp_metadata['userid.attribute'];
172
        } else {
173
            $userid_attributename = 'eduPersonPrincipalName';
174
        }
175
176
        $userids = $attributes[$userid_attributename];
177
178
        if (empty($userids)) {
179
            throw new Exception(sprintf(
180
                'Could not generate useridentifier for storing consent. Attribute [%s] was not available.',
181
                $userid_attributename
182
            ));
183
        }
184
185
        $userid = $userids[0];
186
187
        // Get all SP metadata
188
        $all_sp_metadata = $metadata->getList('saml20-sp-remote');
189
190
        $sp_entityid = $request->get('cv');;
191
        $action = $request->get('action');
192
193
        Logger::critical('consentAdmin: sp: ' . $sp_entityid . ' action: ' . $action);
194
195
        // Remove services, whitch have consent disabled
196
        if (isset($idp_metadata['consent.disable'])) {
197
            foreach ($idp_metadata['consent.disable'] as $disable) {
198
                if (array_key_exists($disable, $all_sp_metadata)) {
199
                    unset($all_sp_metadata[$disable]);
200
                }
201
            }
202
        }
203
204
        Logger::info('consentAdmin: ' . $idp_entityid);
205
206
        // Parse consent config
207
        $consent_storage = $this->store::parseStoreConfig($this->moduleConfig->getValue('consentadmin'));
208
209
        // Calc correct user ID hash
210
        $hashed_user_id = $this->consent::getHashedUserID($userid, $source);
211
212
        // If a checkbox have been clicked
213
        if ($action !== null && $sp_entityid !== null) {
214
            // init template to enable translation of status messages
215
            $template = new Template(
216
                $this->config,
217
                'consentAdmin:consentadminajax.twig',
218
                'consentAdmin:consentadmin'
219
            );
220
221
            // Get SP metadata
222
            $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote');
223
224
            // Run AuthProc filters
225
            list($targeted_id, $attribute_hash, $attributes) = $this->driveProcessingChain(
226
                $idp_metadata,
227
                $source,
228
                $sp_metadata,
229
                $sp_entityid,
230
                $attributes,
231
                $userid,
232
                $hashAttributes,
233
                $excludeAttributes
234
            );
235
236
            // Add a consent (or update if attributes have changed and old consent for SP and IdP exists)
237
            if ($action == 'true') {
238
                $isStored = $consent_storage->saveConsent($hashed_user_id, $targeted_id, $attribute_hash);
239
            } else {
240
                if ($action == 'false') {
241
                    // Got consent, so this is a request to remove it
242
                    $consent_storage->deleteConsent($hashed_user_id, $targeted_id);
243
                    $isStored = false;
244
                } else {
245
                    Logger::info('consentAdmin: unknown action');
246
                    $isStored = null;
247
                }
248
            }
249
            $template->data['isStored'] = $isStored;
250
            return $template;
251
        }
252
253
        // Get all consents for user
254
        $user_consent_list = $consent_storage->getConsents($hashed_user_id);
255
256
        // Parse list of consents
257
        $user_consent = [];
258
        foreach ($user_consent_list as $c) {
259
           $user_consent[$c[0]] = $c[1];
260
        }
261
262
        $template_sp_content = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $template_sp_content is dead and can be removed.
Loading history...
263
264
        // Init template
265
        $template = new Template($this->config, 'consentAdmin:consentadmin.twig', 'consentAdmin:consentadmin');
266
        $template->getLocalization()->addAttributeDomains();
267
        $translator = $template->getTranslator();
0 ignored issues
show
Unused Code introduced by
The assignment to $translator is dead and can be removed.
Loading history...
268
269
        $sp_list = [];
270
271
        // Process consents for all SP
272
        foreach ($all_sp_metadata as $sp_entityid => $sp_values) {
273
            // Get metadata for SP
274
            $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote');
275
276
            // Run attribute filters
277
            list($targeted_id, $attribute_hash, $attributes) = $this->driveProcessingChain(
278
                $idp_metadata,
279
                $source,
280
                $sp_metadata,
281
                $sp_entityid,
282
                $attributes,
283
                $userid,
284
                $hashAttributes,
285
                $excludeAttributes
286
            );
287
288
            // Check if consent exists
289
            if (array_key_exists($targeted_id, $user_consent)) {
290
                $sp_status = "changed";
291
                Logger::info('consentAdmin: changed');
292
                // Check if consent is valid. (Possible that attributes has changed)
293
                if ($user_consent[$targeted_id] == $attribute_hash) {
294
                    Logger::info('consentAdmin: ok');
295
                    $sp_status = "ok";
296
                }
297
                // Consent does not exist
298
            } else {
299
                Logger::info('consentAdmin: none');
300
                $sp_status = "none";
301
            }
302
303
            // Set name of SP
304
            if (isset($sp_values['name']) && is_array($sp_values['name'])) {
305
                $sp_name = $sp_metadata['name'];
306
            } elseif (isset($sp_values['name']) && is_string($sp_values['name'])) {
307
                $sp_name = $sp_metadata['name'];
308
            } elseif (isset($sp_values['OrganizationDisplayName']) && is_array($sp_values['OrganizationDisplayName'])) {
309
                $sp_name = $sp_metadata['OrganizationDisplayName'];
310
            } else {
311
                $sp_name = $sp_entityid;
312
            }
313
314
            // Set description of SP
315
            $sp_description = null;
316
            if (!empty($sp_metadata['description']) && is_array($sp_metadata['description'])) {
317
                $sp_description = $sp_metadata['description'];
318
            }
319
320
            // Add a URL to the service if present in metadata
321
            $sp_service_url = isset($sp_metadata['ServiceURL']) ? $sp_metadata['ServiceURL'] : null;
322
323
            // Fill out array for the template
324
            $sp_list[$sp_entityid] = [
325
                'spentityid'       => $sp_entityid,
326
                'name'             => $sp_name,
327
                'description'      => $sp_description,
328
                'consentStatus'    => $sp_status,
329
                'consentValue'     => $sp_entityid,
330
                'attributes_by_sp' => $attributes,
331
                'serviceurl'       => $sp_service_url,
332
            ];
333
        }
334
335
        $template->data['header'] = 'Consent Administration';
336
        $template->data['spList'] = $sp_list;
337
        $template->data['showDescription'] = $this->moduleConfig->getValue('showDescription');
338
339
        return $template;
340
    }
341
342
343
    /**
344
     * Runs the processing chain and ignores all filter which have user
345
     * interaction.
346
     *
347
     * @param array $idp_metadata
348
     * @param string $source
349
     * @param array $sp_metadata
350
     * @param string $sp_entityid
351
     * @param array $attributes
352
     * @param string $userid
353
     * @param bool $hashAttributes
354
     * @param array $excludeAttributes
355
     * @return array
356
     */
357
    private function driveProcessingChain(
358
        array $idp_metadata,
359
        string $source,
360
        array $sp_metadata,
361
        string $sp_entityid,
362
        array $attributes,
363
        string $userid,
364
        bool $hashAttributes,
365
        array $excludeAttributes
366
    ): array {
367
        /*
368
         * Create a new processing chain
369
         */
370
        $pc = new Auth\ProcessingChain($idp_metadata, $sp_metadata, 'idp');
371
372
        /*
373
         * Construct the state.
374
         * REMEMBER: Do not set Return URL if you are calling processStatePassive
375
         */
376
        $authProcState = [
377
            'Attributes'  => $attributes,
378
            'Destination' => $sp_metadata,
379
            'SPMetadata'  => $sp_metadata,
380
            'Source'      => $idp_metadata,
381
            'IdPMetadata' => $idp_metadata,
382
            'isPassive'   => true,
383
        ];
384
385
        /* we're being bridged, so add that info to the state */
386
        if (strpos($source, '-idp-remote|') !== false) {
387
            /** @var int $i */
388
            $i = strpos($source, '|');
389
            $authProcState['saml:sp:IdP'] = substr($source, $i + 1);
390
        }
391
392
        /*
393
         * Call processStatePAssive.
394
         * We are not interested in any user interaction, only modifications to the attributes
395
         */
396
        $pc->processStatePassive($authProcState);
397
398
        $attributes = $authProcState['Attributes'];
399
        // Remove attributes that do not require consent/should be excluded
400
        foreach ($attributes as $attrkey => $attrval) {
401
            if (in_array($attrkey, $excludeAttributes)) {
402
                unset($attributes[$attrkey]);
403
            }
404
        }
405
406
        /*
407
         * Generate identifiers and hashes
408
         */
409
        $destination = $sp_metadata['metadata-set'] . '|' . $sp_entityid;
410
411
        $targeted_id = $this->consent::getTargetedID($userid, $source, $destination);
412
        $attribute_hash = $this->consent::getAttributeHash($attributes, $hashAttributes);
413
414
        Logger::info('consentAdmin: user: ' . $userid);
415
        Logger::info('consentAdmin: target: ' . $targeted_id);
416
        Logger::info('consentAdmin: attribute: ' . $attribute_hash);
417
418
        // Return values
419
        return [$targeted_id, $attribute_hash, $attributes];
420
    }
421
}
422