UserAPI   F
last analyzed

Complexity

Total Complexity 90

Size/Duplication

Total Lines 592
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 90
eloc 267
c 7
b 0
f 0
dl 0
loc 592
rs 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B generateInstaller() 0 26 6
A verifyDownloadAccess() 0 21 4
B getCache() 0 18 8
B generateNewInstaller() 0 36 6
A processImage() 0 22 3
A downloadInstaller() 0 26 4
A deviceInfo() 0 12 2
B profileAttributes() 0 27 7
B listDevices() 0 21 9
A locateDevice() 0 3 1
A listAllIdentityProviders() 0 3 1
B testForResize() 0 12 7
A orderIdentityProviders() 0 3 1
B getLogo() 0 47 7
A logoExpireTime() 0 5 1
A detectOS() 0 24 6
A returnDevice() 0 13 5
A listIdentityProvidersWithProfiles() 0 2 1
A getUserCerts() 0 18 3
A profileSort() 0 3 1
A deviceFromRequest() 0 17 6

How to fix   Complexity   

Complex Class

Complex classes like UserAPI often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserAPI, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
/**
24
 * This is the collection of methods dedicated for the user GUI
25
 * @author Tomasz Wolniewicz <[email protected]>
26
 * @author Stefan Winter <[email protected]>
27
 * @package UserAPI
28
 *
29
 * Parts of this code are based on simpleSAMLPhp discojuice module.
30
 * This product includes GeoLite data created by MaxMind, available from
31
 * http://www.maxmind.com
32
 */
33
34
namespace core;
35
36
use \Exception;
0 ignored issues
show
Bug introduced by
The type \Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
38
/**
39
 * The basic methoods for the user GUI
40
 * @package UserAPI
41
 *
42
 */
43
class UserAPI extends CAT
44
{
45
46
    /**
47
     * nothing special to be done here.
48
     */
49
    public function __construct()
50
    {
51
        parent::__construct();
52
    }
53
54
    /**
55
     * Prepare the device module environment and send back the link
56
     * This method creates a device module instance via the {@link DeviceFactory} call, 
57
     * then sets up the device module environment for the specific profile by calling 
58
     * {@link DeviceConfig::setup()} method and finally, called the device writeInstaller method
59
     * passing the returned path name.
60
     * 
61
     * @param string $device       identifier as in {@link devices.php}
62
     * @param int    $profileId    profile identifier
63
     * @param string $generatedFor which download area does this pertain to
64
     * @param string $token        for silverbullet: invitation token to consume
65
     * @param string $password     for silverbull: import PIN for the future certificate
66
     *
67
     * @return array|NULL array with the following fields: 
68
     *  profile - the profile identifier; 
69
     *  device - the device identifier; 
70
     *  link - the path name of the resulting installer
71
     *  mime - the mimetype of the installer
72
     */
73
    public function generateInstaller($device, $profileId, $generatedFor = "user", $openRoaming = 0, $token = NULL, $password = NULL)
74
    {
75
        $this->loggerInstance->debug(4, "generateInstaller arguments:$device:$profileId:$openRoaming\n");
76
        $validator = new \web\lib\common\InputValidation();
77
        $profile = $validator->existingProfile($profileId);
78
        // test if the profile is production-ready and if not if the authenticated user is an owner
79
        if ($this->verifyDownloadAccess($profile) === FALSE) {
80
            return;
81
        }
82
        $installerProperties = [];
83
        $installerProperties['profile'] = $profileId;
84
        $installerProperties['device'] = $device;
85
        $cache = $this->getCache($device, $profile, $openRoaming);
86
        $this->installerPath = $cache['path'];
87
        if ($this->installerPath !== NULL && $token === NULL && $password === NULL) {
88
            $this->loggerInstance->debug(4, "Using cached installer for: $device\n");
89
            $installerProperties['link'] = "user/API.php?action=downloadInstaller&lang=".$this->languageInstance->getLang()."&profile=$profileId&device=$device&generatedfor=$generatedFor&openroaming=$openRoaming";
90
            $installerProperties['mime'] = $cache['mime'];
91
        } else {
92
            $myInstaller = $this->generateNewInstaller($device, $profile, $generatedFor, $openRoaming, $token, $password);
93
            if ($myInstaller['link'] !== 0) {
94
                $installerProperties['mime'] = $myInstaller['mime'];
95
            }
96
            $installerProperties['link'] = $myInstaller['link'];
97
        }
98
        return $installerProperties;
99
    }
100
101
    /**
102
     * checks whether the requested profile data is public, XOR was requested by
103
     * its own admin.
104
     * @param \core\AbstractProfile $profile the profile in question
105
     * @return boolean
106
     */
107
    private function verifyDownloadAccess($profile)
108
    {
109
        $attribs = $profile->getCollapsedAttributes();
110
        if (\core\common\Entity::getAttributeValue($attribs, 'profile:production', 0) !== 'on') {
111
            $this->loggerInstance->debug(4, "Attempt to download a non-production ready installer for profile: $profile->identifier\n");
112
            $auth = new \web\lib\admin\Authentication();
113
            if (!$auth->isAuthenticated()) {
114
                $this->loggerInstance->debug(2, "User NOT authenticated, rejecting request for a non-production installer\n");
115
                header("HTTP/1.0 403 Not Authorized");
116
                return FALSE;
117
            }
118
            $auth->authenticate();
119
            $userObject = new User($_SESSION['user']);
120
            if (!$userObject->isIdPOwner($profile->institution)) {
121
                $this->loggerInstance->debug(2, "User not an owner of a non-production profile - access forbidden\n");
122
                header("HTTP/1.0 403 Not Authorized");
123
                return FALSE;
124
            }
125
            $this->loggerInstance->debug(4, "User is the owner - allowing access\n");
126
        }
127
        return TRUE;
128
    }
129
130
    /**
131
     * This function tries to find a cached copy of an installer for a given
132
     * combination of Profile and device
133
     * 
134
     * @param string          $device  the device for which the installer is searched in cache
135
     * @param AbstractProfile $profile the profile for which the installer is searched in cache
136
     * @return array containing path to the installer and mime type of the file, the path is set to NULL if no cache can be returned
137
     */
138
    private function getCache($device, $profile, $openRoaming)
139
    {
140
        $deviceConfig = \devices\Devices::listDevices()[$device];
141
        $noCache = (isset(\devices\Devices::$Options['no_cache']) && \devices\Devices::$Options['no_cache']) ? 1 : 0;
142
        if (isset($deviceConfig['options']['no_cache'])) {
143
            $noCache = $deviceConfig['options']['no_cache'] ? 1 : 0;
144
        }
145
        if ($noCache) {
146
            $this->loggerInstance->debug(5, "getCache: the no_cache option set for this device\n");
147
            return ['path' => NULL, 'mime' => NULL];
148
        }
149
        $this->loggerInstance->debug(5, "getCache: caching option set for this device\n");
150
        $cache = $profile->testCache($device, $openRoaming);
151
        $iPath = $cache['cache'];
152
        if ($iPath && is_file($iPath)) {
153
            return ['path' => $iPath, 'mime' => $cache['mime']];
154
        }
155
        return ['path' => NULL, 'mime' => NULL];
156
    }
157
158
    /**
159
     * Generates a new installer for the given combination of device and Profile
160
     * 
161
     * @param string          $device       the device for which we want an installer
162
     * @param AbstractProfile $profile      the profile for which we want an installer
163
     * @param string          $generatedFor type of download requested (admin/user/silverbullet)
164
     * @param int             $openRoaming values 0 o 1 to indicate support for open roaming in the installer
165
     * @param string          $token        in case of silverbullet, the token that was used to trigger the generation
166
     * @param string          $password     in case of silverbullet, the import PIN for the future client certificate
167
     * @return array info about the new installer (mime and link)
168
     */
169
    private function generateNewInstaller($device, $profile, $generatedFor, $openRoaming, $token, $password)
170
    {
171
        $this->loggerInstance->debug(5, "generateNewInstaller() - Enter");
172
        $this->loggerInstance->debug(5, "generateNewInstaller:openRoaming:$openRoaming\n");
173
        $factory = new DeviceFactory($device);
174
        $this->loggerInstance->debug(5, "generateNewInstaller() - created Device");
175
        $dev = $factory->device;
176
        $out = [];
177
        if (isset($dev)) {
178
            $dev->setup($profile, $token, $password, $openRoaming);
179
            $this->loggerInstance->debug(5, "generateNewInstaller() - Device setup done");
180
            $installer = $dev->writeInstaller();
181
            $this->loggerInstance->debug(5, "generateNewInstaller() - writeInstaller complete");
182
            $iPath = $dev->FPATH.'/tmp/'.$installer;
183
            if ($iPath && is_file($iPath)) {
184
                if (isset($dev->options['mime'])) {
185
                    $out['mime'] = $dev->options['mime'];
186
                } else {
187
                    $info = new \finfo();
188
                    $out['mime'] = $info->file($iPath, FILEINFO_MIME_TYPE);
189
                }
190
                $this->installerPath = $dev->FPATH.'/'.$installer;
191
                rename($iPath, $this->installerPath);
192
                $integerEap = (new \core\common\EAP($dev->selectedEap))->getIntegerRep();
193
                $profile->updateCache($device, $this->installerPath, $out['mime'], $integerEap, $openRoaming);
194
                if (\config\Master::DEBUG_LEVEL < 4) {
195
                    \core\common\Entity::rrmdir($dev->FPATH.'/tmp');
196
                }
197
                $this->loggerInstance->debug(4, "Generated installer: ".$this->installerPath.": for: $device, EAP:".$integerEap.", openRoaming: $openRoaming\n");
198
                $out['link'] = "user/API.php?action=downloadInstaller&lang=".$this->languageInstance->getLang()."&profile=".$profile->identifier."&device=$device&generatedfor=$generatedFor&openroaming=$openRoaming";
199
            } else {
200
                $this->loggerInstance->debug(2, "Installer generation failed for: ".$profile->identifier.":$device:".$this->languageInstance->getLang()."openRoaming: $openRoaming\n");
201
                $out['link'] = 0;
202
            }
203
        }
204
        return $out;
205
    }
206
207
    /**
208
     * interface to Devices::listDevices() 
209
     * 
210
     * @param int $showHidden whether or not hidden devices should be shown
211
     * @return array the list of devices
212
     * @throws Exception
213
     */
214
    public function listDevices($showHidden = 0)
215
    {
216
        $returnList = [];
217
        $count = 0;
218
        if ($showHidden !== 0 && $showHidden != 1) {
219
            throw new Exception("show_hidden is only be allowed to be 0 or 1, but it is $showHidden!");
220
        }
221
        foreach (\devices\Devices::listDevices() as $device => $deviceProperties) {
222
            $hidden = \core\common\Entity::getAttributeValue($deviceProperties, 'options', 'hidden');
223
            if (($hidden === 1 || $hidden === 2) && $showHidden === 0) {
224
                continue;
225
            }
226
            $count++;
227
            $deviceProperties['device'] = $device;
228
            $group = isset($deviceProperties['group']) ? $deviceProperties['group'] : 'other';
229
            if (!isset($returnList[$group])) {
230
                $returnList[$group] = [];
231
            }
232
            $returnList[$group][$device] = $deviceProperties;
233
        }
234
        return $returnList;
235
    }
236
237
    /**
238
     * 
239
     * @param string $device    identifier of the device
240
     * @param int    $profileId identifier of the profile
241
     * @return void
242
     */
243
    public function deviceInfo($device, $profileId)
244
    {
245
        $validator = new \web\lib\common\InputValidation();
246
        $out = 0;
247
        $profile = $validator->existingProfile($profileId);
248
        $factory = new DeviceFactory($device);
249
        $dev = $factory->device;
250
        if (isset($dev)) {
251
            $dev->setup($profile);
252
            $out = $dev->writeDeviceInfo();
253
        }
254
        echo $out;
255
    }
256
257
    /**
258
     * Prepare the support data for a given profile
259
     *
260
     * @param int $profId profile identifier
261
     * @return array
262
     * array with the following fields:
263
     * - local_email
264
     * - local_phone
265
     * - local_url
266
     * - description
267
     * - devices - an array of device names and their statuses (for a given profile)
268
     * - last_changed
269
     */
270
    public function profileAttributes($profId)
271
    {
272
        $validator = new \web\lib\common\InputValidation();
273
        $profile = $validator->existingProfile($profId);
274
        $attribs = $profile->getCollapsedAttributes();
275
        $returnArray = [];
276
        $returnArray['silverbullet'] = $profile instanceof ProfileSilverbullet ? 1 : 0;
277
        if (isset($attribs['support:email'])) {
278
            $returnArray['local_email'] = $attribs['support:email'][0];
279
        }
280
        if (isset($attribs['support:phone'])) {
281
            $returnArray['local_phone'] = $attribs['support:phone'][0];
282
        }
283
        if (isset($attribs['support:url'])) {
284
            $returnArray['local_url'] = $attribs['support:url'][0];
285
        }
286
        if (isset($attribs['profile:description'])) {
287
            $returnArray['description'] = $attribs['profile:description'][0];
288
        }
289
        if (isset($attribs['media:openroaming'])) {
290
            $returnArray['openroaming'] = $attribs['media:openroaming'][0];
291
        } else {
292
            $returnArray['openroaming'] = 'none';
293
        }
294
        $returnArray['devices'] = $profile->listDevices();
295
        $returnArray['last_changed'] = $profile->getFreshness();
296
        return $returnArray;
297
    }
298
299
    /**
300
     * Generate and send the installer
301
     *
302
     * @param string $device        identifier as in {@link devices.php}
303
     * @param int    $prof_id       profile identifier
304
     * @param string $generated_for which download area does this pertain to
305
     * @param string $token         for silverbullet: invitation token to consume
306
     * @param string $password      for silverbull: import PIN for the future certificate
307
     * @return string binary stream: installerFile
308
     */
309
    public function downloadInstaller($device, $prof_id, $generated_for = 'user', $openRoaming = 0, $token = NULL, $password = NULL)
310
    {
311
        $this->loggerInstance->debug(4, "downloadInstaller arguments: $device,$prof_id,$generated_for, $openRoaming\n");
312
        $output = $this->generateInstaller($device, $prof_id, $generated_for, $openRoaming, $token, $password);
313
        $this->loggerInstance->debug(4, "output from GUI::generateInstaller:");
314
        $this->loggerInstance->debug(4, print_r($output, true));
315
        if (empty($output['link']) || $output['link'] === 0) {
316
            header("HTTP/1.0 404 Not Found");
317
            return;
318
        }
319
        $validator = new \web\lib\common\InputValidation();
320
        $profile = $validator->existingProfile($prof_id);
321
        $profile->incrementDownloadStats($device, $generated_for, $openRoaming);
322
        $file = $this->installerPath;
323
        $filetype = $output['mime'];
324
        $this->loggerInstance->debug(4, "installer MIME type:$filetype\n");
325
        header("Content-type: ".$filetype);
326
        if ($filetype !== "application/x-wifi-config") { // for those installers to work on Android, Content-Disposition MUST NOT be set
327
            header('Content-Disposition: inline; filename="'.basename($file).'"');
328
        } else {
329
            header('Content-Transfer-Encoding: base64');
330
        }
331
        header('Content-Length: '.filesize($file));
332
        ob_clean();
333
        flush();
334
        readfile($file);
335
    }
336
337
    /**
338
     * resizes image files
339
     * 
340
     * @param string $inputImage the image we want to process
341
     * @param string $destFile   the output file for the processed image
342
     * @param int    $width      if resizing, the target width
343
     * @param int    $height     if resizing, the target height
344
     * @param bool   $resize     shall we do resizing? width and height are ignored otherwise
345
     * @return array
346
     */
347
    private function processImage($inputImage, $destFile, $width, $height, $resize)
348
    {
349
        $info = new \finfo();
350
        $filetype = $info->buffer($inputImage, FILEINFO_MIME_TYPE);
351
        $expiresString = $this->logoExpireTime();
352
        $blob = $inputImage;
353
354
        if ($resize === TRUE) {
355
            if (class_exists('\\Gmagick')) { 
356
                $image = new \Gmagick(); 
357
            } else {
358
                $image = new \Imagick();
359
            }
360
            $image->readImageBlob($inputImage);
361
            $image->setImageFormat('PNG');
362
            $image->thumbnailImage($width, $height, 1);
363
            $blob = $image->getImageBlob();
0 ignored issues
show
Bug introduced by
The method getImageBlob() does not exist on Gmagick. ( Ignorable by Annotation )

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

363
            /** @scrutinizer ignore-call */ 
364
            $blob = $image->getImageBlob();

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...
364
            $this->loggerInstance->debug(4, "Writing cached logo $destFile for IdP/Federation.\n");
365
            file_put_contents($destFile, $blob);
366
        }
367
368
        return ["filetype" => $filetype, "expires" => $expiresString, "blob" => $blob];
369
    }
370
371
    protected function logoExpireTime()
372
    {
373
        $offset = 60 * 60 * 24 * 30;
374
        // gmdate cannot fail here - time() is its default argument (and integer), and we are adding an integer to it
375
        return("Expires: "./** @scrutinizer ignore-type */ gmdate("D, d M Y H:i:s", time() + $offset)." GMT");
376
    }
377
    /**
378
     * Get and prepare logo file 
379
     *
380
     * When called for DiscoJuice, first check if file cache exists
381
     * If not then generate the file and save it in the cache
382
     * @param int|string $identifier IdP or Federation identifier
383
     * @param string     $type       either 'idp' or 'federation' is allowed 
384
     * @param integer    $widthIn    maximum width of the generated image - if 0 then it is treated as no upper bound
385
     * @param integer    $heightIn   maximum height of the generated image - if 0 then it is treated as no upper bound
386
     * @return array|null array with image information or NULL if there is no logo
387
     * @throws Exception
388
     */
389
    protected function getLogo($identifier, $type, $widthIn, $heightIn)
390
    {
391
        $expiresString = '';
392
        $attributeName = [
393
            'federation' => "fed:logo_file",
394
            'federation_from_idp' => "fed:logo_file",
395
            'idp' => "general:logo_file",
396
        ];
397
398
        $logoFile = "";
399
        $validator = new \web\lib\common\InputValidation();
400
        switch ($type) {
401
            case "federation":
402
                $entity = $validator->existingFederation($identifier);
403
                break;
404
            case "idp":
405
                $entity = $validator->existingIdP($identifier);
406
                break;
407
            case "federation_from_idp":
408
                $idp = $validator->existingIdP($identifier);
409
                $entity = $validator->existingFederation($idp->federation);
410
                break;
411
            default:
412
                throw new Exception("Unknown type of logo requested!");
413
        }
414
        $filetype = 'image/png'; // default, only one code path where it can become different
415
        list($width, $height, $resize) = $this->testForResize($widthIn, $heightIn);
416
        if ($resize) {
417
            $logoFile = ROOT.'/web/downloads/logos/'.$identifier.'_'.$width.'_'.$height.'.png';
418
        }
419
        if (is_file($logoFile)) { // $logoFile could be an empty string but then we will get a FALSE
420
            $this->loggerInstance->debug(4, "Using cached logo $logoFile for: $identifier\n");
421
            $blob = file_get_contents($logoFile);
422
        } else {
423
            $logoAttribute = $entity->getAttributes($attributeName[$type]);
424
            if (count($logoAttribute) == 0) {
425
                $blob = file_get_contents(ROOT.'/web/resources/images/empty.png');
426
                $expiresString = $this->logoExpireTime();
427
            } else {
428
                $this->loggerInstance->debug(4, "RESIZE:$width:$height\n");
429
                $meta = $this->processImage($logoAttribute[0]['value'], $logoFile, $width, $height, $resize);
430
                $filetype = $meta['filetype'];
431
                $expiresString = $meta['expires'];
432
                $blob = $meta['blob'];
433
            }
434
        }
435
        return ["filetype" => $filetype, "expires" => $expiresString, "blob" => $blob];
436
    }
437
438
    /**
439
     * see if we have to resize an image
440
     * 
441
     * @param integer $width  the desired max width (0 = unbounded)
442
     * @param integer $height the desired max height (0 = unbounded)
443
     * @return array
444
     */
445
    private function testForResize($width, $height)
446
    {
447
        if (is_numeric($width) && is_numeric($height) && ($width > 0 || $height > 0)) {
448
            if ($height == 0) {
449
                $height = 10000;
450
            }
451
            if ($width == 0) {
452
                $width = 10000;
453
            }
454
            return [$width, $height, TRUE];
455
        }
456
        return [0, 0, FALSE];
457
    }
458
459
    /**
460
     * find out where the device is currently located
461
     * @return array
462
     */
463
    public function locateDevice()
464
    {
465
        return \core\DeviceLocation::locateDevice();
466
    }
467
468
    /**
469
     * Lists all identity providers in the database
470
     * adding information required by DiscoJuice.
471
     * 
472
     * @param int    $activeOnly if set to non-zero will cause listing of only those institutions which have some valid profiles defined.
473
     * @param string $country    if set, only list IdPs in a specific country
474
     * @return array the list of identity providers
475
     *
476
     */
477
    public function listAllIdentityProviders($activeOnly = 0, $country = "")
478
    {
479
        return IdPlist::listAllIdentityProviders($activeOnly, $country);
480
    }
481
482
    /**
483
     * Order active identity providers according to their distance and name
484
     * @param string $country         NRO to work with
485
     * @param array  $currentLocation current location
486
     *
487
     * @return array $IdPs -  list of arrays ('id', 'name');
488
     */
489
    public function orderIdentityProviders($country, $currentLocation)
490
    {
491
        return IdPlist::orderIdentityProviders($country, $currentLocation);
492
    }
493
    
494
    /**
495
     * outputs a full list of IdPs containing the fllowing data:
496
     * institution_is, institution name in all available languages,
497
     * list of production profiles.
498
     * For eache profile the profile identifier, profile name in all languages
499
     * and redirect values (empty rediret value means that no redirect has been
500
     * set).
501
     * 
502
     * @return array of identity providers with attributes
503
     */
504
    public function listIdentityProvidersWithProfiles() {
505
        return IdPlist::listIdentityProvidersWithProfiles();
506
    }
507
    
508
    /**
509
     * Detect the best device driver form the browser
510
     * Detects the operating system and returns its id 
511
     * display name and group membership (as in devices.php)
512
     * @return array|boolean OS information, indexed by 'id', 'display', 'group'
513
     */
514
    public function detectOS()
515
    {
516
        $Dev = \devices\Devices::listDevices();
517
        $devId = $this->deviceFromRequest();
518
        if ($devId !== NULL) {
519
            $ret = $this->returnDevice($devId, $Dev[$devId]);
520
            if ($ret !== FALSE) {
521
                return $ret;
522
            }
523
        }
524
// the device has not been specified or not specified correctly, try to detect if from the browser ID
525
        $browser = htmlspecialchars(strip_tags($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES);
526
        $this->loggerInstance->debug(4, "HTTP_USER_AGENT=$browser\n");
527
        foreach ($Dev as $devId => $device) {
528
            if (!isset($device['match'])) {
529
                continue;
530
            }
531
            if (preg_match('/'.$device['match'].'/', $browser)) {
532
                $this->loggerInstance->debug(5, "Matched: $devId\n".$device['match']."\n".$browser."\n");
533
                return $this->returnDevice($devId, $device);
534
            }
535
        }
536
        $this->loggerInstance->debug(2, "Unrecognised system: $browser\n");
537
        return FALSE;
538
    }
539
540
    /**
541
     * test if devise is defined and is not hidden. If all is fine return extracted information.
542
     * 
543
     * @param string $devId  device id as defined as index in Devices.php
544
     * @param array  $device device info as defined in Devices.php
545
     * @return array|FALSE if the device has not been correctly specified
546
     */
547
    private function returnDevice($devId, $device)
548
    {
549
        $hidden = \core\common\Entity::getAttributeValue($device, 'options', 'hidden');
550
        if ($hidden !== 1 && $hidden !== 2) {
551
            $this->loggerInstance->debug(4, "Browser_id: $devId\n");
552
            if (isset($device['options']['hs20']) && $device['options']['hs20'] === 1) {
553
                $hs20 = 1;
554
            } else {
555
                $hs20 = 0;
556
            }
557
            return ['device' => $devId, 'display' => $device['display'], 'group' => $device['group'], 'hs20' => $hs20];
558
        }
559
        return FALSE;
560
    }
561
562
    /**
563
     * This method checks if the device has been specified as the HTTP parameters
564
     * 
565
     * @return device id|NULL if correctly specified or FALSE otherwise
566
     */
567
    private function deviceFromRequest()
568
    {
569
        $devId = NULL;
570
        if (isset($_GET['device'])) {
571
            $devId = htmlspecialchars(strip_tags($_GET['device']));
572
        } elseif (isset($_POST['device'])) {
573
            $devId = htmlspecialchars(strip_tags($_POST['device']));
574
        } 
575
        if ($devId === NULL || $devId === FALSE) {
576
            $this->loggerInstance->debug(4, "Invalid device id provided\n");
577
            return NULL;
578
        }
579
        if (!isset(\devices\Devices::listDevices()[$devId])) {
580
            $this->loggerInstance->debug(2, "Unrecognised system: $devId\n");
581
            return NULL;
582
        }
583
        return $devId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $devId returns the type string which is incompatible with the documented return type core\device.
Loading history...
584
    }
585
586
    /**
587
     * finds all the user certificates that originated in a given token
588
     * 
589
     * @param string $token the token for which we are fetching all associated user certs
590
     * @return array|boolean returns FALSE if a token is invalid, otherwise array of certs
591
     */
592
    public function getUserCerts($token)
593
    {
594
        $validator = new \web\lib\common\InputValidation();
595
        $cleanToken = $validator->token($token);
596
        if ($cleanToken) {
597
            // check status of this silverbullet token according to info in DB:
598
            // it can be VALID (exists and not redeemed, EXPIRED, REDEEMED or INVALID (non existent)
599
            $invitationObject = new \core\SilverbulletInvitation($cleanToken);
600
        } else {
601
            return false;
602
        }
603
        $profile = new \core\ProfileSilverbullet($invitationObject->profile, NULL);
604
        $userdata = $profile->userStatus($invitationObject->userId);
605
        $allcerts = [];
606
        foreach ($userdata as $content) {
607
            $allcerts = array_merge($allcerts, $content->associatedCertificates);
608
        }
609
        return $allcerts;
610
    }
611
612
    /**
613
     * device name
614
     * 
615
     * @var string
616
     */
617
    public $device;
618
619
    /**
620
     * path to installer
621
     * 
622
     * @var string
623
     */
624
    private $installerPath;
625
626
    /**
627
     * helper function to sort profiles by their name
628
     * @param \core\AbstractProfile $profile1 the first profile's information
629
     * @param \core\AbstractProfile $profile2 the second profile's information
630
     * @return int
631
     */
632
    private static function profileSort($profile1, $profile2)
633
    {
634
        return strcasecmp($profile1->name, $profile2->name);
635
    }
636
}
637