Passed
Pull Request — master (#3)
by Tim
03:41 queued 59s
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
        // Init template
263
        $template = new Template($this->config, 'consentAdmin:consentadmin.twig', 'consentAdmin:consentadmin');
264
        $template->getLocalization()->addAttributeDomains();
265
266
        $sp_list = [];
267
268
        // Process consents for all SP
269
        foreach ($all_sp_metadata as $sp_entityid => $sp_values) {
270
            // Get metadata for SP
271
            $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote');
272
273
            // Run attribute filters
274
            list($targeted_id, $attribute_hash, $attributes) = $this->driveProcessingChain(
275
                $idp_metadata,
276
                $source,
277
                $sp_metadata,
278
                $sp_entityid,
279
                $attributes,
280
                $userid,
281
                $hashAttributes,
282
                $excludeAttributes
283
            );
284
285
            // Check if consent exists
286
            if (array_key_exists($targeted_id, $user_consent)) {
287
                $sp_status = "changed";
288
                Logger::info('consentAdmin: changed');
289
                // Check if consent is valid. (Possible that attributes has changed)
290
                if ($user_consent[$targeted_id] == $attribute_hash) {
291
                    Logger::info('consentAdmin: ok');
292
                    $sp_status = "ok";
293
                }
294
                // Consent does not exist
295
            } else {
296
                Logger::info('consentAdmin: none');
297
                $sp_status = "none";
298
            }
299
300
            // Set name of SP
301
            if (isset($sp_values['name']) && is_array($sp_values['name'])) {
302
                $sp_name = $sp_metadata['name'];
303
            } elseif (isset($sp_values['name']) && is_string($sp_values['name'])) {
304
                $sp_name = $sp_metadata['name'];
305
            } elseif (isset($sp_values['OrganizationDisplayName']) && is_array($sp_values['OrganizationDisplayName'])) {
306
                $sp_name = $sp_metadata['OrganizationDisplayName'];
307
            } else {
308
                $sp_name = $sp_entityid;
309
            }
310
311
            // Set description of SP
312
            $sp_description = null;
313
            if (!empty($sp_metadata['description']) && is_array($sp_metadata['description'])) {
314
                $sp_description = $sp_metadata['description'];
315
            }
316
317
            // Add a URL to the service if present in metadata
318
            $sp_service_url = isset($sp_metadata['ServiceURL']) ? $sp_metadata['ServiceURL'] : null;
319
320
            // Fill out array for the template
321
            $sp_list[$sp_entityid] = [
322
                'spentityid'       => $sp_entityid,
323
                'name'             => $sp_name,
324
                'description'      => $sp_description,
325
                'consentStatus'    => $sp_status,
326
                'consentValue'     => $sp_entityid,
327
                'attributes_by_sp' => $attributes,
328
                'serviceurl'       => $sp_service_url,
329
            ];
330
        }
331
332
        $template->data['header'] = 'Consent Administration';
333
        $template->data['spList'] = $sp_list;
334
        $template->data['showDescription'] = $this->moduleConfig->getValue('showDescription');
335
336
        return $template;
337
    }
338
339
340
    /**
341
     * Runs the processing chain and ignores all filter which have user
342
     * interaction.
343
     *
344
     * @param array $idp_metadata
345
     * @param string $source
346
     * @param array $sp_metadata
347
     * @param string $sp_entityid
348
     * @param array $attributes
349
     * @param string $userid
350
     * @param bool $hashAttributes
351
     * @param array $excludeAttributes
352
     * @return array
353
     */
354
    private function driveProcessingChain(
355
        array $idp_metadata,
356
        string $source,
357
        array $sp_metadata,
358
        string $sp_entityid,
359
        array $attributes,
360
        string $userid,
361
        bool $hashAttributes,
362
        array $excludeAttributes
363
    ): array {
364
        /*
365
         * Create a new processing chain
366
         */
367
        $pc = new Auth\ProcessingChain($idp_metadata, $sp_metadata, 'idp');
368
369
        /*
370
         * Construct the state.
371
         * REMEMBER: Do not set Return URL if you are calling processStatePassive
372
         */
373
        $authProcState = [
374
            'Attributes'  => $attributes,
375
            'Destination' => $sp_metadata,
376
            'SPMetadata'  => $sp_metadata,
377
            'Source'      => $idp_metadata,
378
            'IdPMetadata' => $idp_metadata,
379
            'isPassive'   => true,
380
        ];
381
382
        /* we're being bridged, so add that info to the state */
383
        if (strpos($source, '-idp-remote|') !== false) {
384
            /** @var int $i */
385
            $i = strpos($source, '|');
386
            $authProcState['saml:sp:IdP'] = substr($source, $i + 1);
387
        }
388
389
        /*
390
         * Call processStatePAssive.
391
         * We are not interested in any user interaction, only modifications to the attributes
392
         */
393
        $pc->processStatePassive($authProcState);
394
395
        $attributes = $authProcState['Attributes'];
396
        // Remove attributes that do not require consent/should be excluded
397
        foreach ($attributes as $attrkey => $attrval) {
398
            if (in_array($attrkey, $excludeAttributes)) {
399
                unset($attributes[$attrkey]);
400
            }
401
        }
402
403
        /*
404
         * Generate identifiers and hashes
405
         */
406
        $destination = $sp_metadata['metadata-set'] . '|' . $sp_entityid;
407
408
        $targeted_id = $this->consent::getTargetedID($userid, $source, $destination);
409
        $attribute_hash = $this->consent::getAttributeHash($attributes, $hashAttributes);
410
411
        Logger::info('consentAdmin: user: ' . $userid);
412
        Logger::info('consentAdmin: target: ' . $targeted_id);
413
        Logger::info('consentAdmin: attribute: ' . $attribute_hash);
414
415
        // Return values
416
        return [$targeted_id, $attribute_hash, $attributes];
417
    }
418
}
419