Passed
Push — master ( 32b95a...2c2bb9 )
by Stefan
05:45 queued 29s
created

DeviceConfig::translateFile()   C

Complexity

Conditions 7
Paths 48

Size

Total Lines 32
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
c 0
b 0
f 0
rs 6.7272
cc 7
eloc 23
nc 48
nop 3
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
/**
13
 * This file defines the abstract Device class
14
 *
15
 * @package ModuleWriting
16
 */
17
/**
18
 * 
19
 */
20
21
namespace core;
22
23
use \Exception;
24
25
/**
26
 * This class defines the API for CAT module writers.
27
 *
28
 * A device is a fairly abstract notion. In most cases it represents
29
 * a particular operating system or a set of operationg systems
30
 * like MS Windows Vista and newer.
31
 *
32
 * The purpose of this class is to preapare a setup for the device configurator,
33
 * collect all necessary information from the database, taking into account
34
 * limitations, that a given device may present (like a set of supported EAP methods).
35
 *
36
 * All that is required from the device module is to produce a conigurator
37
 * file and pass its name back to the API.
38
 *
39
 * 
40
 * @author Tomasz Wolniewicz <[email protected]>
41
 *
42
 * @license see LICENSE file in root directory
43
 * 
44
 * @package ModuleWriting
45
 * @abstract
46
 */
47
abstract class DeviceConfig extends \core\common\Entity {
48
49
    /**
50
     * stores the path to the temporary working directory for a module instance
51
     * @var string $FPATH
52
     */
53
    public $FPATH;
54
55
    /**
56
     * array of specialities - will be displayed on the admin download as "footnote"
57
     * @var array specialities
58
     */
59
    public $specialities;
60
61
    /**
62
     * list of supported EAP methods
63
     * @var array EAP methods
64
     */
65
    public $supportedEapMethods;
66
67
    /**
68
     * the custom displayable variant of the term 'federation'
69
     * @var string
70
     */
71
    public $nomenclature_fed;
72
    
73
    /**
74
     * the custom displayable variant of the term 'institution'
75
     * @var string
76
     */
77
    public $nomenclature_inst;
78
    
79
    /**
80
     * 
81
     * @param array $eapArray the list of EAP methods the device supports
82
     */
83
    protected function setSupportedEapMethods($eapArray) {
84
        $this->supportedEapMethods = $eapArray;
85
        $this->loggerInstance->debug(4, "This device (" . __CLASS__ . ") supports the following EAP methods: ");
86
        $this->loggerInstance->debug(4, $this->supportedEapMethods, true);
0 ignored issues
show
Unused Code introduced by
The call to core\common\Logging::debug() has too many arguments starting with true. ( Ignorable by Annotation )

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

86
        $this->loggerInstance->/** @scrutinizer ignore-call */ 
87
                               debug(4, $this->supportedEapMethods, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
87
    }
88
89
    /**
90
     * device module constructor should be defined by each module. 
91
     * The one important thing to do is to call setSupportedEapMethods with an 
92
     * array of EAP methods the device supports
93
     */
94 View Code Duplication
    public function __construct() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
95
        parent::__construct();
96
        // some config elements are displayable. We need some dummies to 
97
        // translate the common values for them. If a deployment chooses a 
98
        // different wording, no translation, sorry
99
100
        $dummy_NRO = _("National Roaming Operator");
101
        $dummy_inst1 = _("identity provider");
102
        $dummy_inst2 = _("organisation");
103
        // and do something useless with the strings so that there's no "unused" complaint
104
        $dummy_NRO = $dummy_NRO . $dummy_inst1 . $dummy_inst2;
0 ignored issues
show
Unused Code introduced by
The assignment to $dummy_NRO is dead and can be removed.
Loading history...
105
106
        $this->nomenclature_fed = _(CONFIG_CONFASSISTANT['CONSORTIUM']['nomenclature_federation']);
107
        $this->nomenclature_inst = _(CONFIG_CONFASSISTANT['CONSORTIUM']['nomenclature_institution']);
108
    }
109
110
    /**
111
     * generates a UUID, for the devices which identify file contents by UUID
112
     *
113
     * @param string $prefix an extra prefix to set before the UUID
114
     * @return string UUID (possibly prefixed)
115
     */
116
    public function uuid($prefix = '', $deterministicSource = NULL) {
117
        if ($deterministicSource === NULL) {
118
            $chars = md5(uniqid(mt_rand(), true));
119
        } else {
120
            $chars = md5($deterministicSource);
121
        }
122
        // these substr() are guaranteed to yield actual string data, as the
123
        // base string is an MD5 hash - has sufficient length
124
        $uuid = /** @scrutinizer ignore-type */ substr($chars, 0, 8) . '-';
125
        $uuid .= /** @scrutinizer ignore-type */ substr($chars, 8, 4) . '-';
126
        $uuid .= /** @scrutinizer ignore-type */ substr($chars, 12, 4) . '-';
127
        $uuid .= /** @scrutinizer ignore-type */ substr($chars, 16, 4) . '-';
128
        $uuid .= /** @scrutinizer ignore-type */ substr($chars, 20, 12);
129
        return $prefix . $uuid;
130
    }
131
132
    /**
133
     * Set up working environment for a device module
134
     *
135
     * Sets up the device module environment taking into account the actual profile
136
     * selected by the user in the GUI. The selected profile is passed as the
137
     * Profile $profile argumant.
138
     *
139
     * This method needs to be called after the device instance has been created (the GUI class does that)
140
     *
141
     * setup performs the following tasks:
142
     * - collect profile attributes and pass them as the attributes property;
143
     * - create the temporary working directory
144
     * - process CA certificates and store them as 'internal:CAs' attribute
145
     * - process and save optional info files and store references to them in
146
     *   'internal:info_file' attribute
147
     * @param AbstractProfile $profile the profile object which will be passed by the caller
148
     * @final not to be redefined
149
     */
150
    final public function setup(AbstractProfile $profile, $token = NULL, $importPassword = NULL) {
151
        $this->loggerInstance->debug(4, "module setup start\n");
152
        $purpose = 'installer';
153
        if (!$profile instanceof AbstractProfile) {
154
            $this->loggerInstance->debug(2, "No profile has been set\n");
155
            throw new Exception("No profile has been set");
156
        }
157
158
        $eaps = $profile->getEapMethodsinOrderOfPreference(1);
159
        $this->calculatePreferredEapType($eaps);
160
        if (count($this->selectedEap) == 0) {
161
            throw new Exception("No EAP type specified.");
162
        }
163
        $this->attributes = $this->getProfileAttributes($profile);
164
        $this->deviceUUID = $this->uuid('', 'CAT' . $profile->institution . "-" . $profile->identifier . "-" . $this->device_id);
165
166
167
        // if we are instantiating a Silverbullet profile AND have been given
168
        // a token, attempt to create the client certificate NOW
169
        // then, this is the only instance of the device ever which knows the
170
        // cert and private key. It's not saved anywhere, so it's gone forever
171
        // after code execution!
172
173
        $this->loggerInstance->debug(5, "DeviceConfig->setup() - preliminaries done.\n");
174
        if ($profile instanceof ProfileSilverbullet && $token !== NULL && $importPassword !== NULL) {
175
            $this->clientCert = $profile->issueCertificate($token, $importPassword);
176
            // add a UUID identifier for the devices that want one
177
            $this->clientCert['GUID'] = $this->uuid("", $this->clientCert['certdata']);
178
            // we need to drag this along; ChromeOS needs it outside the P12 container to encrypt the entire *config* with it.
179
            // Because encrypted private keys are not supported as per spec!
180
            $purpose = 'silverbullet';
181
            // let's keep a record for which device type this token was consumed
182
            $dbInstance = DBConnection::handle("INST");
183
            $devicename = \devices\Devices::listDevices()[$this->device_id]['display'];
184
            $certId = $this->clientCert['certificateId'];
185
            $dbInstance->exec("UPDATE `silverbullet_certificate` SET `device` = ? WHERE `id` = ?", "si", $devicename, $certId);    
186
        }
187
        $this->loggerInstance->debug(5, "DeviceConfig->setup() - silverbullet checks done.\n");
188
        // create temporary directory, its full path will be saved in $this->FPATH;
189
        $tempDir = $this->createTemporaryDirectory($purpose);
190
        $this->FPATH = $tempDir['dir'];
191
        mkdir($tempDir['dir'] . '/tmp');
192
        chdir($tempDir['dir'] . '/tmp');
193
        $caList = [];
194
        $x509 = new \core\common\X509();
195
        if (isset($this->attributes['eap:ca_file'])) {
196
            foreach ($this->attributes['eap:ca_file'] as $ca) {
197
                $processedCert = $x509->processCertificate($ca);
198
                if (is_array($processedCert)) {
199
                    // add a UUID for convenience (some devices refer to their CAs by a UUID value)
200
                    $processedCert['uuid'] = $this->uuid("", $processedCert['pem']);
201
                    $caList[] = $processedCert;
202
                }
203
            }
204
            $this->attributes['internal:CAs'][0] = $caList;
205
        }
206
207
        if (isset($this->attributes['support:info_file'])) {
208
            $this->attributes['internal:info_file'][0] = $this->saveInfoFile($this->attributes['support:info_file'][0]);
209
        }
210 View Code Duplication
        if (isset($this->attributes['general:logo_file'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
211
            $this->loggerInstance->debug(5, "saving IDP logo\n");
212
            $this->attributes['internal:logo_file'] = $this->saveLogoFile($this->attributes['general:logo_file'],'idp');
213
        }
214 View Code Duplication
        if (isset($this->attributes['fed:logo_file'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
            $this->loggerInstance->debug(5, "saving FED logo\n");
216
            $this->attributes['fed:logo_file'] = $this->saveLogoFile($this->attributes['fed:logo_file'], 'fed');
217
        }
218
        $this->attributes['internal:SSID'] = $this->getSSIDs()['add'];
219
220
        $this->attributes['internal:remove_SSID'] = $this->getSSIDs()['del'];
221
222
        $this->attributes['internal:consortia'] = $this->getConsortia();
223
        $olddomain = $this->languageInstance->setTextDomain("core");
224
        $this->support_email_substitute = sprintf(_("your local %s support"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
225
        $this->support_url_substitute = sprintf(_("your local %s support page"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
226
        $this->languageInstance->setTextDomain($olddomain);
227
228
        if ($this->signer && $this->options['sign']) {
229
            $this->sign = ROOT . '/signer/' . $this->signer;
230
        }
231
        $this->installerBasename = $this->getInstallerBasename();
232
    }
233
234
    /**
235
     * Selects the preferred eap method based on profile EAP configuration and device EAP capabilities
236
     *
237
     * @param array $eapArrayofObjects an array of eap methods supported by a given device
238
     */
239
    public function calculatePreferredEapType($eapArrayofObjects) {
240
        $this->selectedEap = [];
241
        foreach ($eapArrayofObjects as $eap) {
242
            if (in_array($eap->getArrayRep(), $this->supportedEapMethods)) {
243
                $this->selectedEap = $eap->getArrayRep();
244
            }
245
        }
246
        if ($this->selectedEap != []) {
247
            $this->selectedEapObject = new common\EAP($this->selectedEap);
248
        }
249
    }
250
251
    /**
252
     * prepare usage information for the installer
253
     * every device module should override this method
254
     *
255
     * @return String HTML text to be displayed
256
     */
257
    public function writeDeviceInfo() {
258
        return _("Sorry, this should not happen - no additional information is available");
259
    }
260
    
261
    private function findSourceFile($file) {
262
        if (is_file($this->module_path . '/Files/' . $this->device_id . '/' . $file)) {
263
            return $this->module_path . '/Files/' . $this->device_id . '/' . $file;
264
        } elseif (is_file($this->module_path . '/Files/' . $file)) {
265
            return $this->module_path . '/Files/' . $file;
266
        } else {
267
            $this->loggerInstance->debug(2, "requested file $file does not exist\n");
268
            return(FALSE);
269
        }
270
    }
271
272
    /**
273
     *  Copy a file from the module location to the temporary directory.
274
     *
275
     * If the second argument is provided then the file will be saved under the name 
276
     * taken form this argument. If only one parameter is given, source and destination
277
     * filenames are the same
278
     * Source file can be located either in the Files subdirectory or in the sibdirectory of Files
279
     * named the same as device_id. The second option takes precedence.
280
     *
281
     * @param string $source_name The source file name
282
     * @param string $output_name The destination file name
283
     *
284
     * @return bool result of the copy operation
285
     * @final not to be redefined
286
     */
287
    final protected function copyFile($source_name, $output_name = NULL) {
288
        if ($output_name === NULL) {
289
            $output_name = $source_name;
290
        }
291
        $this->loggerInstance->debug(5, "fileCopy($source_name, $output_name)\n");
292
        $source = $this->findSourceFile($source_name);
293
        if ($source === FALSE) {
294
            return FALSE;
295
        }
296
        $this->loggerInstance->debug(5, "Copying $source to $output_name\n");
297
        $result = copy($source, "$output_name");
298
        if (!$result) {
299
            $this->loggerInstance->debug(2, "fileCopy($source_name, $output_name) failed\n");
300
        }
301
        return($result);
302
    }
303
304
    /**
305
     *  Copy a file from the module location to the temporary directory aplying transcoding.
306
     *
307
     * Transcoding is only required for Windows installers, and no Unicode support
308
     * in NSIS (NSIS version below 3)
309
     * Trancoding is only applied if the third optional parameter is set and nonzero
310
     * If CONFIG['NSIS']_VERSION is set to 3 or more, no transcoding will be applied
311
     * regardless of the third parameter value.
312
     * If the second argument is provided and is not equal to 0, then the file will be
313
     * saved under the name taken from this argument.
314
     * If only one parameter is given or the second is equal to 0, source and destination
315
     * filenames are the same.
316
     * The third optional parameter, if nonzero, should be the character set understood by iconv
317
     * This is required by the Windows installer and is expected to go away in the future.
318
     * Source file can be located either in the Files subdirectory or in the sibdirectory of Files
319
     * named the same as device_id. The second option takes precedence.
320
     *
321
     * @param string $source_name The source file name
322
     * @param string $output_name The destination file name
323
     * @param int $encoding Set Windows charset if non-zero
324
     *
325
     * @final not to be redefined
326
     */
327
    final protected function translateFile($source_name, $output_name = NULL, $encoding = 0) {
328
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
329
            $encoding = 0;
330
        }
331
        if ($output_name === NULL) {
332
            $output_name = $source_name;
333
        }
334
335
        $this->loggerInstance->debug(5, "translateFile($source_name, $output_name, $encoding)\n");
336
        ob_start();
337
        $this->loggerInstance->debug(5, $this->module_path . '/Files/' . $this->device_id . '/' . $source_name . "\n");
338
        $source = $this->findSourceFile($source_name);
339
        
340
        if ($source !== FALSE) { // if there is no file found, don't attempt to include an uninitialised variable
341
            include($source);
342
        }
343
        $output = ob_get_clean();
344
        if ($encoding) {
345
            $outputClean = iconv('UTF-8', $encoding . '//TRANSLIT', $output);
346
            if ($outputClean) {
347
                $output = $outputClean;
348
            }
349
        }
350
        $fileHandle = fopen("$output_name", "w");
351
        if (!$fileHandle) {
352
            $this->loggerInstance->debug(2, "translateFile($source, $output_name, $encoding) failed\n");
353
            return FALSE;
354
        }
355
        fwrite($fileHandle, $output);
356
        fclose($fileHandle);
357
        $this->loggerInstance->debug(5, "translateFile($source, $output_name, $encoding) end\n");
358
        return TRUE;
359
    }
360
361
    /**
362
     * Transcode a string adding double quotes escaping
363
     *
364
     * Transcoding is only required for Windows installers, and no Unicode support
365
     * in NSIS (NSIS version below 3)
366
     * Trancoding is only applied if the third optional parameter is set and nonzero
367
     * If CONFIG['NSIS']_VERSION is set to 3 or more, no transcoding will be applied
368
     * regardless of the second parameter value.
369
     * The second optional parameter, if nonzero, should be the character set understood by iconv
370
     * This is required by the Windows installer and is expected to go away in the future.
371
     *
372
     * @param string $source_string The source string
373
     * @param int $encoding Set Windows charset if non-zero
374
     *
375
     * @final not to be redefined
376
     */
377
    final protected function translateString($source_string, $encoding = 0) {
378
        $this->loggerInstance->debug(5, "translateString input: \"$source_string\"\n");
379
        if (empty($source_string)) {
380
            return($source_string);
381
        }
382
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
383
            $encoding = 0;
384
        }
385
        if ($encoding) {
386
            $output_c = iconv('UTF-8', $encoding . '//TRANSLIT', $source_string);
387
        } else {
388
            $output_c = $source_string;
389
        }
390
        if ($output_c) {
391
            $source_string = str_replace('"', '$\\"', $output_c);
392
        } else {
393
            $this->loggerInstance->debug(2, "Failed to convert string \"$source_string\"\n");
394
        }
395
        return $source_string;
396
    }
397
398
    /**
399
     * Save certificate files in either DER or PEM format
400
     *
401
     * Certificate files will be saved in the module working directory.
402
     * @param string $format  only "der" and "pem" are currently allowed
403
     * @return array an array of arrays or FALSE on error
404
     * saved certificate file names are avalable under the 'file' index
405
     * additional array entries are indexed as 'sha1', 'md5', and 'root'.
406
     * sha1 and md5 are correcponding certificate hashes
407
     * root is set to 1 for the CA roor certicicate and 0 otherwise
408
     */
409
    final protected function saveCertificateFiles($format) {
410
        switch ($format) {
411
            case "der": // fall-thorugh, same treatment
412
            case "pem":
413
                $iterator = 0;
414
                $caFiles = [];
415
                $caArray = $this->attributes['internal:CAs'][0];
416
                if (!$caArray) {
417
                    return(FALSE);
418
                }
419
                foreach ($caArray as $certAuthority) {
420
                    $fileHandle = fopen("cert-$iterator.crt", "w");
421
                    if (!$fileHandle) {
422
                        throw new Exception("problem opening the file");
423
                    }
424
                    if ($format === "pem") {
425
                        fwrite($fileHandle, $certAuthority['pem']);
426
                    } else {
427
                        fwrite($fileHandle, $certAuthority['der']);
428
                    }
429
                    fclose($fileHandle);
430
                    $certAuthorityProps = [];
431
                    $certAuthorityProps['file'] = "cert-$iterator.crt";
432
                    $certAuthorityProps['sha1'] = $certAuthority['sha1'];
433
                    $certAuthorityProps['md5'] = $certAuthority['md5'];
434
                    $certAuthorityProps['root'] = $certAuthority['root'];
435
                    $caFiles[] = $certAuthorityProps;
436
                    $iterator++;
437
                }
438
                return($caFiles);
439
            default:
440
                $this->loggerInstance->debug(2, 'incorrect format value specified');
441
                return(FALSE);
442
        }
443
    }
444
445
    /**
446
     * Generate installer filename base.
447
     * Device module should use this name adding an extension.
448
     * Normally the device identifier follows the Consortium name.
449
     * The sting taken for the device identifier equals (by default) to the index in the listDevices array,
450
     * but can be overriden with the 'device_id' device option.
451
     */
452
    private function getInstallerBasename() {
453
        $replace_pattern = '/[ ()\/\'"]+/';
454
        $consortiumName = iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace($replace_pattern, '_', CONFIG_CONFASSISTANT['CONSORTIUM']['name']));
455
        if (isset($this->attributes['profile:customsuffix'][1])) { 
456
            // this string will end up as a filename on a filesystem, so always
457
            // take a latin-based language variant if available
458
            // and then scrub non-ASCII just in case
459
            return $consortiumName . "-" . $this->getDeviceId() . iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace($replace_pattern, '_', $this->attributes['profile:customsuffix'][1]));
460
        }
461
        // Okay, no custom suffix. 
462
        // Use the configured inst name and apply shortening heuristics
463
        $lang_pointer = CONFIG['LANGUAGES'][$this->languageInstance->getLang()]['latin_based'] == TRUE ? 0 : 1;
464
        $this->loggerInstance->debug(5, "getInstallerBasename1:" . $this->attributes['general:instname'][$lang_pointer] . "\n");
465
        $inst = iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace($replace_pattern, '_', $this->attributes['general:instname'][$lang_pointer]));
466
        $this->loggerInstance->debug(4, "getInstallerBasename2:$inst\n");
467
        $Inst_a = explode('_', $inst);
468
        if (count($Inst_a) > 2) {
469
            $inst = '';
470
            foreach ($Inst_a as $i) {
471
                $inst .= $i[0];
472
            }
473
        }
474
        // and if the inst has multiple profiles, add the profile name behin
475
        if ($this->attributes['internal:profile_count'][0] > 1) {
476
            if (!empty($this->attributes['profile:name']) && !empty($this->attributes['profile:name'][$lang_pointer])) {
477
                $profTemp = iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace($replace_pattern, '_', $this->attributes['profile:name'][$lang_pointer]));
478
                $prof = preg_replace('/_+$/', '', $profTemp);
479
                return $consortiumName . '-' . $this->getDeviceId() . $inst . '-' . $prof;
480
            }
481
        }
482
        return $consortiumName . '-' . $this->getDeviceId() . $inst;
483
    }
484
485
    private function getDeviceId() {
486
        $deviceId = $this->device_id;
487
        if (isset($this->options['device_id'])) {
488
            $deviceId = $this->options['device_id'];
489
        }
490
        if ($deviceId !== '') {
491
            $deviceId .= '-';
492
        }
493
        return $deviceId;
494
    }
495
496
    private function getSSIDs() {
497
        $ssidList = [];
498
        $ssidList['add'] = [];
499
        $ssidList['del'] = [];
500
        if (isset(CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) {
501
            foreach (CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'] as $ssid) {
502
                if (isset(CONFIG_CONFASSISTANT['CONSORTIUM']['tkipsupport']) && CONFIG_CONFASSISTANT['CONSORTIUM']['tkipsupport'] == TRUE) {
503
                    $ssidList['add'][$ssid] = 'TKIP';
504
                } else {
505
                    $ssidList['add'][$ssid] = 'AES';
506
                    $ssidList['del'][$ssid] = 'TKIP';
507
                }
508
            }
509
        }
510 View Code Duplication
        if (isset($this->attributes['media:SSID'])) {
511
            $ssidWpa2 = $this->attributes['media:SSID'];
512
513
            foreach ($ssidWpa2 as $ssid) {
514
                $ssidList['add'][$ssid] = 'AES';
515
            }
516
        }
517 View Code Duplication
        if (isset($this->attributes['media:SSID_with_legacy'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
518
            $ssidTkip = $this->attributes['media:SSID_with_legacy'];
519
            foreach ($ssidTkip as $ssid) {
520
                $ssidList['add'][$ssid] = 'TKIP';
521
            }
522
        }
523 View Code Duplication
        if (isset($this->attributes['media:remove_SSID'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
524
            $ssidRemove = $this->attributes['media:remove_SSID'];
525
            foreach ($ssidRemove as $ssid) {
526
                $ssidList['del'][$ssid] = 'DEL';
527
            }
528
        }
529
        return $ssidList;
530
    }
531
532
    private function getConsortia() {
533
        $consortia = CONFIG_CONFASSISTANT['CONSORTIUM']['interworking-consortium-oi'];
534
        if (isset($this->attributes['media:consortium_OI'])) {
535
            foreach ($this->attributes['media:consortium_OI'] as $new_oi) {
536
                $consortia[] = $new_oi;
537
            }
538
        }
539
        return $consortia;
540
    }
541
542
    /**
543
     * An array with shorthand definitions for MIME types
544
     * @var array
545
     */
546
    private $mime_extensions = [
547
        'text/plain' => 'txt',
548
        'text/rtf' => 'rtf',
549
        'application/pdf' => 'pdf',
550
    ];
551
552
    private function saveLogoFile($logos,$type) {
553
        $iterator = 0;
554
        $returnarray = [];
555
        foreach ($logos as $blob) {
556
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
557
            $mime = $finfo->buffer($blob);
558
            $matches = [];
559
            if (preg_match('/^image\/(.*)/', $mime, $matches)) {
560
                $ext = $matches[1];
561
            } else {
562
                $ext = 'unsupported';
563
            }
564
            $this->loggerInstance->debug(5, "saveLogoFile: $mime : $ext\n");
565
            $fileName = 'logo-' . $type . $iterator . '.' . $ext;
566
            $fileHandle = fopen($fileName, "w");
567
            if (!$fileHandle) {
568
                $this->loggerInstance->debug(2, "saveLogoFile failed for: $fileName\n");
569
                throw new Exception("problem opening the file");
570
            }
571
            fwrite($fileHandle, $blob);
572
            fclose($fileHandle);
573
            $returnarray[] = ['name' => $fileName, 'mime' => $ext];
574
            $iterator++;
575
        }
576
        return($returnarray);
577
    }
578
579
    private function saveInfoFile($blob) {
580
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
581
        $mime = $finfo->buffer($blob);
582
        $ext = isset($this->mime_extensions[$mime]) ? $this->mime_extensions[$mime] : 'usupported';
583
        $this->loggerInstance->debug(5, "saveInfoFile: $mime : $ext\n");
584
        $fileHandle = fopen('local-info.' . $ext, "w");
585
        if (!$fileHandle) {
586
            throw new Exception("problem opening the file");
587
        }
588
        fwrite($fileHandle, $blob);
589
        fclose($fileHandle);
590
        return(['name' => 'local-info.' . $ext, 'mime' => $ext]);
591
    }
592
593
    private function getProfileAttributes(AbstractProfile $profile) {
594
        $bestMatchEap = $this->selectedEap;
595
        if (count($bestMatchEap) > 0) {
596
            $a = $profile->getCollapsedAttributes($bestMatchEap);
597
            $a['eap'] = $bestMatchEap;
598
            $a['all_eaps'] = $profile->getEapMethodsinOrderOfPreference(1);
599
            return($a);
600
        }
601
        print("No supported eap types found for this profile.\n");
602
        return [];
603
    }
604
605
    /**
606
     * dumps attributes for debugging purposes
607
     *
608
     * dumpAttibutes method is supplied for debuging purposes, it simply dumps the attribute array
609
     * to a file with name passed in the attribute.
610
     * @param string $file the output file name
611
     */
612
    protected function dumpAttibutes($file) {
613
        ob_start();
614
        print_r($this->attributes);
615
        $output = ob_get_clean();
616
        file_put_contents($file, $output);
617
    }
618
619
    /**
620
     * placeholder for the main device method
621
     *
622
     */
623
    protected function writeInstaller() {
624
        return("download path");
625
    }
626
627
    protected function determineOuterIdString() {
628
        $outerId = 0;
629
        if (isset($this->attributes['internal:use_anon_outer']) && $this->attributes['internal:use_anon_outer'][0] == "1" && isset($this->attributes['internal:realm'])) {
630
            $outerId = "@" . $this->attributes['internal:realm'][0];
631
            if (isset($this->attributes['internal:anon_local_value'])) {
632
                $outerId = $this->attributes['internal:anon_local_value'][0] . $outerId;
633
            }
634
        }
635
        return $outerId;
636
    }
637
638
    /**
639
     * Array passing all options to the device module.
640
     *
641
     * $attrbutes array contains option values defined for the institution and a particular
642
     * profile (possibly overriding one another) ready for the device module to consume.
643
     * 
644
     * For each of the options the value is another array of vales (even if only one value is present).
645
     * Some attributes may be missing if they have not been configured for a viven institution or profile.
646
     *
647
     * The following attributes are meant to be used by device modules:
648
     * - <b>general:geo_coordinates</b> -  geographical coordinates of the institution or a campus
649
     * - <b>support:info_file</b>  -  consent file displayed to the users                                                         
650
     * - <b>general:logo_file</b>  -  file data containing institution logo                                                      
651
     * - <b>support:eap_types</b>  -  URL to a local support page for a specific eap methiod, not to be confused with general:url 
652
     * - <b>support:email</b>      -  email for users to contact for local instructions                                           
653
     * - <b>support:phone</b>      -  telephone number for users to contact for local instructions                                
654
     * - <b>support:url</b>        -  URL where the user will find local instructions       
655
     * - <b>internal:info_file</b> -  the pathname of the info_file saved in the working directory
656
     * - <b>internal:logo_file</b>  -  array of pathnames of logo_files saved in the working directory
657
     * - <b>internal:CAs</b> - the value is an array produced by X509::processCertificate() with the following filds
658
     * - <b>internal:SSID</b> - an array indexed by SSID strings with values either TKIP or AES; if TKIP is set the both WPA/TKIP and WPA2/AES should be set if AES is set the this is a WPA2/AES only SSID; the consortium's defined SSIDs are always set as the first array elements.
659
     * -<b>internal:profile_count</b> - the number of profiles for the associated IdP
660
     *
661
     *
662
     * these attributes are available and can be used, but the "internal" attributes are better suited for modules
663
     * -  eap:ca_file    -      certificate of the CA signing the RADIUS server key                                         
664
     * - <b>media:SSID</b>       -  additional SSID to configure, WPA2/AES only (device modules should use internal:SSID)
665
     * - <b>media:SSID_with_legacy</b> -  additional SSID to configure, WPA2/AES and WPA/TKIP (device modules should use internal:SSID)
666
     *
667
     * @see \core\common\X509::processCertificate()
668
     * @var array $attributes
669
     */
670
    public $attributes;
671
672
    /**
673
     * stores the path to the module source location and is used 
674
     * by copyFile and translateFile
675
     * the only reason for it to be a public variable ies that it is set by the DeviceFactory class
676
     * module_path should not be used by module drivers.
677
     * @var string 
678
     */
679
    public $module_path;
680
681
    /**
682
     * The optimal EAP type
683
     *
684
     */
685
686
    /**
687
     * optimal EAP method selected given profile and device
688
     * @var array
689
     */
690
    public $selectedEap;
691
    public $selectedEapObject;
692
693
    /**
694
     * the path to the profile signing program
695
     * device modules which require signing should use this property to exec the signer
696
     * the signer program must accept two arguments - input and output file names
697
     * the signer program mus operate in the local directory and filenames are relative to this
698
     * directory
699
     *
700
     * @var string
701
     */
702
    public $sign;
703
    public $signer;
704
705
    /**
706
     * The string identifier of the device (don't show this to users)
707
     * @var string
708
     */
709
    public $device_id;
710
711
    /**
712
     * See devices-template.php for a list of available options
713
     * @var array
714
     */
715
    public $options;
716
717
    /**
718
     * This string will be shown if no support email was configured by the admin
719
     * 
720
     * @var string 
721
     */
722
    public $support_email_substitute;
723
724
    /**
725
     * This string will be shown if no support URL was configured by the admin
726
     * 
727
     * @var string 
728
     */
729
    public $support_url_substitute;
730
731
    /**
732
     * This string should be used by all installer modules to set the 
733
     * installer file basename.
734
     *
735
     * @var string 
736
     */
737
    public $installerBasename;
738
739
    /**
740
     * stores the PKCS#12 DER representation of a client certificate for SilverBullet
741
     */
742
    protected $clientCert;
743
744
    /**
745
     * stores identifier used by GEANTLink profiles
746
     */
747
    public $deviceUUID;
748
749
}
750