Passed
Push — release_2_0 ( dd2425...0861a7 )
by Stefan
07:22
created

DeviceConfig::longestNameSuffix()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 15
c 2
b 0
f 0
dl 0
loc 27
rs 8.8333
cc 7
nc 5
nop 0
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 file defines the abstract Device class
25
 *
26
 * @package ModuleWriting
27
 */
28
/**
29
 * 
30
 */
31
32
namespace core;
33
34
use \Exception;
35
36
/**
37
 * This class defines the API for CAT module writers.
38
 *
39
 * A device is a fairly abstract notion. In most cases it represents
40
 * a particular operating system or a set of operationg systems
41
 * like MS Windows Vista and newer.
42
 *
43
 * The purpose of this class is to preapare a setup for the device configurator,
44
 * collect all necessary information from the database, taking into account
45
 * limitations, that a given device may present (like a set of supported EAP methods).
46
 *
47
 * All that is required from the device module is to produce a conigurator
48
 * file and pass its name back to the API.
49
 *
50
 * 
51
 * @author Tomasz Wolniewicz <[email protected]>
52
 *
53
 * @license see LICENSE file in root directory
54
 * 
55
 * @package ModuleWriting
56
 * @abstract
57
 */
58
abstract class DeviceConfig extends \core\common\Entity {
59
60
    /**
61
     * stores the path to the temporary working directory for a module instance
62
     * @var string $FPATH
63
     */
64
    public $FPATH;
65
66
    /**
67
     * array of specialities - will be displayed on the admin download as "footnote"
68
     * @var array specialities
69
     */
70
    public $specialities;
71
72
    /**
73
     * list of supported EAP methods
74
     * @var array EAP methods
75
     */
76
    public $supportedEapMethods;
77
78
    /**
79
     * sets the supported EAP methods for a device
80
     * 
81
     * @param array $eapArray the list of EAP methods the device supports
82
     * @return void
83
     */
84
    protected function setSupportedEapMethods($eapArray) {
85
        $this->supportedEapMethods = $eapArray;
86
        $this->loggerInstance->debug(4, "This device (" . __CLASS__ . ") supports the following EAP methods: ");
87
        $this->loggerInstance->debug(4, $this->supportedEapMethods);
88
    }
89
90
    /**
91
     * device module constructor should be defined by each module. 
92
     * The one important thing to do is to call setSupportedEapMethods with an 
93
     * array of EAP methods the device supports
94
     */
95
    public function __construct() {
96
        parent::__construct();
97
    }
98
99
    /**
100
     * given one or more server name strings, calculate the suffix that is common
101
     * to all of them
102
     * 
103
     * Examples:
104
     * 
105
     * ["host.somewhere.com", "gost.somewhere.com"] => "ost.somewhere.com"
106
     * ["my.server.name"] => "my.server.name"
107
     * ["foo.bar.de", "baz.bar.ge"] => "e"
108
     * ["server1.example.com", "server2.example.com", "serverN.example.com"] => ".example.com"
109
110
     * @return string
111
     */
112
    public function longestNameSuffix() {
113
        // for all configured server names, find the string that is the longest
114
        // suffix to all of them
115
        $longestSuffix = "";
116
        if (!isset($this->attributes["eap:server_name"])) {
117
            return "";
118
        }
119
        $numStrings = count($this->attributes["eap:server_name"]);
120
        if ($numStrings == 0) {
121
            return "";
122
        }
123
        // always take the candidate character from the first array element, and
124
        // verify whether the other elements have that character in the same 
125
        // position, too
126
        while (TRUE) {
127
            if ($longestSuffix == $this->attributes["eap:server_name"][0]) {
128
                break;
129
            }
130
            $candidate = substr($this->attributes["eap:server_name"][0], -(strlen($longestSuffix) + 1), 1);
131
            for ($iterator = 1; $iterator < $numStrings; $iterator++) {
132
                if (substr($this->attributes["eap:server_name"][$iterator], -(strlen($longestSuffix) + 1), 1) != $candidate) {
133
                    break 2;
134
                }
135
            }
136
            $longestSuffix = $candidate . $longestSuffix;
137
        }
138
        return $longestSuffix;
139
    }
140
141
    /**
142
     * Set up working environment for a device module
143
     *
144
     * Sets up the device module environment taking into account the actual profile
145
     * selected by the user in the GUI. The selected profile is passed as the
146
     * Profile $profile argumant.
147
     *
148
     * This method needs to be called after the device instance has been created (the GUI class does that)
149
     *
150
     * setup performs the following tasks:
151
     * - collect profile attributes and pass them as the attributes property;
152
     * - create the temporary working directory
153
     * - process CA certificates and store them as 'internal:CAs' attribute
154
     * - process and save optional info files and store references to them in
155
     *   'internal:info_file' attribute
156
     * @param AbstractProfile $profile        the profile object which will be passed by the caller
157
     * @param string          $token          the invitation token for silverbullet requests
158
     * @param string          $importPassword the PIN for the installer for silverbullet requests
159
     * @return void
160
     * @final not to be redefined
161
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
162
    final public function setup(AbstractProfile $profile, $token = NULL, $importPassword = NULL) {
163
        $this->loggerInstance->debug(4, "module setup start\n");
164
        common\Entity::intoThePotatoes();
165
        $purpose = 'installer';
166
        $eaps = $profile->getEapMethodsinOrderOfPreference(1);
167
        $this->calculatePreferredEapType($eaps);
168
        if (count($this->selectedEap) == 0) {
169
            throw new Exception("No EAP type available.");
170
        }
171
        $this->attributes = $this->getProfileAttributes($profile);
172
        $this->deviceUUID = common\Entity::uuid('', 'CAT' . $profile->institution . "-" . $profile->identifier . "-" . $this->device_id);
173
174
175
        // if we are instantiating a Silverbullet profile AND have been given
176
        // a token, attempt to create the client certificate NOW
177
        // then, this is the only instance of the device ever which knows the
178
        // cert and private key. It's not saved anywhere, so it's gone forever
179
        // after code execution!
180
181
        $this->loggerInstance->debug(5, "DeviceConfig->setup() - preliminaries done.\n");
182
        if ($profile instanceof ProfileSilverbullet && $token !== NULL && $importPassword !== NULL) {
183
            $this->clientCert = SilverbulletCertificate::issueCertificate($token, $importPassword, $this->options['clientcert']);
184
            // we need to drag this along; ChromeOS needs it outside the P12 container to encrypt the entire *config* with it.
185
            // Because encrypted private keys are not supported as per spec!
186
            $purpose = 'silverbullet';
187
            // let's keep a record for which device type this token was consumed
188
            $dbInstance = DBConnection::handle("INST");
189
            $certId = $this->clientCert['certObject']->dbId;
190
            $dbInstance->exec("UPDATE `silverbullet_certificate` SET `device` = ? WHERE `id` = ?", "si", $this->device_id, $certId);
191
        }
192
        $this->loggerInstance->debug(5, "DeviceConfig->setup() - silverbullet checks done.\n");
193
        // create temporary directory, its full path will be saved in $this->FPATH;
194
        $tempDir = $this->createTemporaryDirectory($purpose);
195
        $this->FPATH = $tempDir['dir'];
196
        mkdir($tempDir['dir'] . '/tmp');
197
        chdir($tempDir['dir'] . '/tmp');
198
        $caList = [];
199
        $x509 = new \core\common\X509();
200
        if (isset($this->attributes['eap:ca_file'])) {
201
            foreach ($this->attributes['eap:ca_file'] as $ca) {
202
                $processedCert = $x509->processCertificate($ca);
203
                if (is_array($processedCert)) {
204
                    // add a UUID for convenience (some devices refer to their CAs by a UUID value)
205
                    $processedCert['uuid'] = common\Entity::uuid("", $processedCert['pem']);
206
                    $caList[] = $processedCert;
207
                }
208
            }
209
            $this->attributes['internal:CAs'][0] = $caList;
210
        }
211
212
        if (isset($this->attributes['support:info_file'])) {
213
            $this->attributes['internal:info_file'][0] = $this->saveInfoFile($this->attributes['support:info_file'][0]);
214
        }
215
        if (isset($this->attributes['general:logo_file'])) {
216
            $this->loggerInstance->debug(5, "saving IDP logo\n");
217
            $this->attributes['internal:logo_file'] = $this->saveLogoFile($this->attributes['general:logo_file'], 'idp');
218
        }
219
        if (isset($this->attributes['fed:logo_file'])) {
220
            $this->loggerInstance->debug(5, "saving FED logo\n");
221
            $this->attributes['fed:logo_file'] = $this->saveLogoFile($this->attributes['fed:logo_file'], 'fed');
222
        }
223
        $this->attributes['internal:SSID'] = $this->getSSIDs()['add'];
224
225
        $this->attributes['internal:remove_SSID'] = $this->getSSIDs()['del'];
226
227
        $this->attributes['internal:consortia'] = $this->getConsortia();
228
229
        $this->support_email_substitute = sprintf(_("your local %s support"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
230
        $this->support_url_substitute = sprintf(_("your local %s support page"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
231
232
        if ($this->signer && $this->options['sign']) {
233
            $this->sign = ROOT . '/signer/' . $this->signer;
234
        }
235
        $this->installerBasename = $this->getInstallerBasename();
236
        common\Entity::outOfThePotatoes();
237
    }
238
239
    /**
240
     * Selects the preferred eap method based on profile EAP configuration and device EAP capabilities
241
     *
242
     * @param array $eapArrayofObjects an array of eap methods supported by a given device
243
     * @return void
244
     */
245
    public function calculatePreferredEapType($eapArrayofObjects) {
246
        $this->selectedEap = [];
247
        foreach ($eapArrayofObjects as $eap) {
248
            if (in_array($eap->getArrayRep(), $this->supportedEapMethods)) {
249
                $this->selectedEap = $eap->getArrayRep();
250
                break;
251
            }
252
        }
253
        if ($this->selectedEap != []) {
254
            $this->selectedEapObject = new common\EAP($this->selectedEap);
255
        }
256
    }
257
258
    /**
259
     * prepare usage information for the installer
260
     * every device module should override this method
261
     *
262
     * @return string HTML text to be displayed
263
     */
264
    public function writeDeviceInfo() {
265
        common\Entity::intoThePotatoes();
266
        $retval = _("Sorry, this should not happen - no additional information is available");
267
        common\Entity::outOfThePotatoes();
268
        return $retval;
269
    }
270
271
    /**
272
     * function to return exactly one attribute type
273
     * 
274
     * @param string $attrName the attribute to retrieve
275
     * @return array|NULL the attributes
276
     */
277
    public function getAttribute($attrName) {
278
        return empty($this->attributes[$attrName]) ? NULL : $this->attributes[$attrName];
279
    }
280
281
    /**
282
     * some modules have a complex directory structure. This helper finds resources
283
     * in that structure. Mostly used in the Windows modules.
284
     * 
285
     * @param  string $file the filename to search for (without path)
286
     * @return string|boolean the filename as found, with path, or FALSE if it does not exist
287
     */
288
    protected function findSourceFile($file) {
289
        if (is_file($this->module_path . '/Files/' . $this->device_id . '/' . $file)) {
290
            return $this->module_path . '/Files/' . $this->device_id . '/' . $file;
291
        } elseif (is_file($this->module_path . '/Files/' . $file)) {
292
            return $this->module_path . '/Files/' . $file;
293
        } else {
294
            $this->loggerInstance->debug(2, "requested file $file does not exist\n");
295
            return FALSE;
296
        }
297
    }
298
299
    /**
300
     *  Copy a file from the module location to the temporary directory.
301
     *
302
     * If the second argument is provided then the file will be saved under the name 
303
     * taken form this argument. If only one parameter is given, source and destination
304
     * filenames are the same
305
     * Source file can be located either in the Files subdirectory or in the sibdirectory of Files
306
     * named the same as device_id. The second option takes precedence.
307
     *
308
     * @param string $source_name The source file name
309
     * @param string $output_name The destination file name
310
     *
311
     * @return boolean result of the copy operation
312
     * @final not to be redefined
313
     */
314
    final protected function copyFile($source_name, $output_name = NULL) {
315
        if ($output_name === NULL) {
316
            $output_name = $source_name;
317
        }
318
        $this->loggerInstance->debug(5, "fileCopy($source_name, $output_name)\n");
319
        $source = $this->findSourceFile($source_name);
320
        if ($source === FALSE) {
321
            return FALSE;
322
        }
323
        $this->loggerInstance->debug(5, "Copying $source to $output_name\n");
324
        $result = copy($source, "$output_name");
325
        if (!$result) {
326
            $this->loggerInstance->debug(2, "fileCopy($source_name, $output_name) failed\n");
327
        }
328
        return($result);
329
    }
330
331
    /**
332
     * Save certificate files in either DER or PEM format
333
     *
334
     * Certificate files will be saved in the module working directory.
335
     * 
336
     * saved certificate file names are avalable under the 'file' index
337
     * additional array entries are indexed as 'sha1', 'md5', and 'root'.
338
     * sha1 and md5 are correcponding certificate hashes
339
     * root is set to 1 for the CA roor certicicate and 0 otherwise
340
     * 
341
     * @param string $format only "der" and "pem" are currently allowed
342
     * @return array an array of arrays or empty array on error
343
344
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
345
    final protected function saveCertificateFiles($format) {
346
        switch ($format) {
347
            case "der": // fall-thorugh, same treatment
348
            case "pem":
349
                $iterator = 0;
350
                $caFiles = [];
351
                $caArray = $this->attributes['internal:CAs'][0];
352
                if (!$caArray) {
353
                    return([]);
354
                }
355
                foreach ($caArray as $certAuthority) {
356
                    $fileHandle = fopen("cert-$iterator.crt", "w");
357
                    if (!$fileHandle) {
358
                        throw new Exception("problem opening the file");
359
                    }
360
                    if ($format === "pem") {
361
                        fwrite($fileHandle, $certAuthority['pem']);
362
                    } else {
363
                        fwrite($fileHandle, $certAuthority['der']);
364
                    }
365
                    fclose($fileHandle);
366
                    $certAuthorityProps = [];
367
                    $certAuthorityProps['file'] = "cert-$iterator.crt";
368
                    $certAuthorityProps['sha1'] = $certAuthority['sha1'];
369
                    $certAuthorityProps['md5'] = $certAuthority['md5'];
370
                    $certAuthorityProps['root'] = $certAuthority['root'];
371
                    $caFiles[] = $certAuthorityProps;
372
                    $iterator++;
373
                }
374
                return($caFiles);
375
            default:
376
                $this->loggerInstance->debug(2, 'incorrect format value specified');
377
                return([]);
378
        }
379
    }
380
381
    /**
382
     * set of characters to remove from filename strings
383
     */
384
    private const TRANSLIT_SCRUB = '/[ ()\/\'"]+/';
385
386
    /**
387
     * Does a transliteration from UTF-8 to ASCII to get a sane filename
388
     * Takes special characters into account, and always uses English CTYPE
389
     * to avoid introduction of funny characters due to "flying accents"
390
     * 
391
     * @param string $input the input string that is to be transliterated
392
     * @return string the transliterated string
393
     */
394
    private function customTranslit($input) {
395
        $oldlocale = setlocale(LC_CTYPE, 0);
396
        setlocale(LC_CTYPE, "en_US.UTF-8");
397
        $retval = preg_replace(DeviceConfig::TRANSLIT_SCRUB, '_', iconv("UTF-8", "US-ASCII//TRANSLIT", $input));
398
        setlocale(LC_CTYPE, $oldlocale);
399
        return $retval;
400
    }
401
402
    /**
403
     * Generate installer filename base.
404
     * Device module should use this name adding an extension.
405
     * Normally the device identifier follows the Consortium name.
406
     * The sting taken for the device identifier equals (by default) to the index in the listDevices array,
407
     * but can be overriden with the 'device_id' device option.
408
     * 
409
     * @return string
410
     */
411
    private function getInstallerBasename() {
412
413
        $baseName = $this->customTranslit(CONFIG_CONFASSISTANT['CONSORTIUM']['name']) . "-" . $this->getDeviceId();
414
        if (isset($this->attributes['profile:customsuffix'][1])) {
415
            // this string will end up as a filename on a filesystem, so always
416
            // take a latin-based language variant if available
417
            // and then scrub non-ASCII just in case
418
            return $baseName . $this->customTranslit($this->attributes['profile:customsuffix'][1]);
419
        }
420
        // Okay, no custom suffix. 
421
        // Use the configured inst name and apply shortening heuristics
422
        $lang_pointer = CONFIG['LANGUAGES'][$this->languageInstance->getLang()]['latin_based'] == TRUE ? 0 : 1;
423
        $this->loggerInstance->debug(5, "getInstallerBasename1:" . $this->attributes['general:instname'][$lang_pointer] . "\n");
424
        $inst = $this->customTranslit($this->attributes['general:instname'][$lang_pointer]);
425
        $this->loggerInstance->debug(4, "getInstallerBasename2:$inst\n");
426
        $Inst_a = explode('_', $inst);
427
        if (count($Inst_a) > 2) {
428
            $inst = '';
429
            foreach ($Inst_a as $i) {
430
                $inst .= $i[0];
431
            }
432
        }
433
        // and if the inst has multiple profiles, add the profile name behin
434
        if ($this->attributes['internal:profile_count'][0] > 1) {
435
            if (!empty($this->attributes['profile:name']) && !empty($this->attributes['profile:name'][$lang_pointer])) {
436
                $profTemp = $this->customTranslit($this->attributes['profile:name'][$lang_pointer]);
437
                $prof = preg_replace('/_+$/', '', $profTemp);
438
                return $baseName . $inst . '-' . $prof;
439
            }
440
        }
441
        return $baseName . $inst;
442
    }
443
444
    /**
445
     * returns the device_id of the current device
446
     * 
447
     * @return string
448
     */
449
    private function getDeviceId() {
450
        $deviceId = $this->device_id;
451
        if (isset($this->options['device_id'])) {
452
            $deviceId = $this->options['device_id'];
453
        }
454
        if ($deviceId !== '') {
455
            $deviceId .= '-';
456
        }
457
        return $deviceId;
458
    }
459
460
    /**
461
     * returns the list of SSIDs that installers should treat. 
462
     * 
463
     * Includes both SSIDs to be set up (and whether it's a TKIP-mixed or AES-only SSID) and SSIDs to be deleted
464
     * 
465
     * @return array
466
     */
467
    private function getSSIDs() {
468
        $ssidList = [];
469
        $ssidList['add'] = [];
470
        $ssidList['del'] = [];
471
        if (isset(CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) {
472
            foreach (CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'] as $ssid) {
473
                if (\core\common\Entity::getAttributeValue(CONFIG_CONFASSISTANT, 'CONSORTIUM', 'tkipsupport') == TRUE) {
474
                    $ssidList['add'][$ssid] = 'TKIP';
475
                } else {
476
                    $ssidList['add'][$ssid] = 'AES';
477
                    $ssidList['del'][$ssid] = 'TKIP';
478
                }
479
            }
480
        }
481
        if (isset($this->attributes['media:SSID'])) {
482
            $ssidWpa2 = $this->attributes['media:SSID'];
483
484
            foreach ($ssidWpa2 as $ssid) {
485
                $ssidList['add'][$ssid] = 'AES';
486
            }
487
        }
488
        if (isset($this->attributes['media:SSID_with_legacy'])) {
489
            $ssidTkip = $this->attributes['media:SSID_with_legacy'];
490
            foreach ($ssidTkip as $ssid) {
491
                $ssidList['add'][$ssid] = 'TKIP';
492
            }
493
        }
494
        if (isset($this->attributes['media:remove_SSID'])) {
495
            $ssidRemove = $this->attributes['media:remove_SSID'];
496
            foreach ($ssidRemove as $ssid) {
497
                $ssidList['del'][$ssid] = 'DEL';
498
            }
499
        }
500
        return $ssidList;
501
    }
502
503
    /**
504
     * returns the list of Hotspot 2.0 / Passpoint roaming consortia to set up
505
     * 
506
     * @return array
507
     */
508
    private function getConsortia() {
509
        if (!isset(CONFIG_CONFASSISTANT['CONSORTIUM']['interworking-consortium-oi'])) {
510
            return ([]);
511
        }
512
        $consortia = CONFIG_CONFASSISTANT['CONSORTIUM']['interworking-consortium-oi'];
513
        if (isset($this->attributes['media:consortium_OI'])) {
514
            foreach ($this->attributes['media:consortium_OI'] as $new_oi) {
515
                if (!in_array($new_oi, $consortia)) {
516
                    $consortia[] = $new_oi;
517
                }
518
            }
519
        }
520
        return $consortia;
521
    }
522
523
    /**
524
     * An array with shorthand definitions for MIME types
525
     * @var array
526
     */
527
    private $mime_extensions = [
528
        'text/plain' => 'txt',
529
        'text/rtf' => 'rtf',
530
        'application/pdf' => 'pdf',
531
    ];
532
533
    /**
534
     * saves a number of logos to a cache directory on disk.
535
     * 
536
     * @param array  $logos list of logos (binary strings each)
537
     * @param string $type  a qualifier what type of logo this is
538
     * @return array list of filenames and the mime types
539
     * @throws Exception
540
     */
541
    private function saveLogoFile($logos, $type) {
542
        $iterator = 0;
543
        $returnarray = [];
544
        foreach ($logos as $blob) {
545
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
546
            $mime = $finfo->buffer($blob);
547
            $matches = [];
548
            if (preg_match('/^image\/(.*)/', $mime, $matches)) {
549
                $ext = $matches[1];
550
            } else {
551
                $ext = 'unsupported';
552
            }
553
            $this->loggerInstance->debug(5, "saveLogoFile: $mime : $ext\n");
554
            $fileName = 'logo-' . $type . $iterator . '.' . $ext;
555
            $fileHandle = fopen($fileName, "w");
556
            if (!$fileHandle) {
557
                $this->loggerInstance->debug(2, "saveLogoFile failed for: $fileName\n");
558
                throw new Exception("problem opening the file");
559
            }
560
            fwrite($fileHandle, $blob);
561
            fclose($fileHandle);
562
            $returnarray[] = ['name' => $fileName, 'mime' => $ext];
563
            $iterator++;
564
        }
565
        return($returnarray);
566
    }
567
568
    /**
569
     * saves the Terms of Use file onto disk
570
     * 
571
     * @param string $blob the Terms of Use
572
     * @return array with one entry, containging the filename and mime type
573
     * @throws Exception
574
     */
575
    private function saveInfoFile($blob) {
576
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
577
        $mime = $finfo->buffer($blob);
578
        $ext = isset($this->mime_extensions[$mime]) ? $this->mime_extensions[$mime] : 'usupported';
579
        $this->loggerInstance->debug(5, "saveInfoFile: $mime : $ext\n");
580
        $fileHandle = fopen('local-info.' . $ext, "w");
581
        if ($fileHandle === FALSE) {
582
            throw new Exception("problem opening the file");
583
        }
584
        fwrite($fileHandle, $blob);
585
        fclose($fileHandle);
586
        return(['name' => 'local-info.' . $ext, 'mime' => $ext]);
587
    }
588
589
    /**
590
     * returns the attributes of the profile for which to generate an installer
591
     * 
592
     * In condensed notion, and most specific level only (i.e. ignores overriden attributes from a higher level)
593
     * @param \core\AbstractProfile $profile the Profile in question
594
     * @return array
595
     */
596
    private function getProfileAttributes(AbstractProfile $profile) {
597
        $bestMatchEap = $this->selectedEap;
598
        if (count($bestMatchEap) > 0) {
599
            $a = $profile->getCollapsedAttributes($bestMatchEap);
600
            $a['eap'] = $bestMatchEap;
601
            $a['all_eaps'] = $profile->getEapMethodsinOrderOfPreference(1);
602
            return($a);
603
        }
604
        print("No supported eap types found for this profile.\n");
605
        return [];
606
    }
607
608
    /**
609
     * dumps attributes for debugging purposes
610
     *
611
     * dumpAttibutes method is supplied for debuging purposes, it simply dumps the attribute array
612
     * to a file with name passed in the attribute.
613
     * @param string $file the output file name
614
     * @return void
615
     */
616
    protected function dumpAttibutes($file) {
617
        ob_start();
618
        print_r($this->attributes);
619
        $output = ob_get_clean();
620
        file_put_contents($file, $output);
621
    }
622
623
    /**
624
     * placeholder for the main device method
625
     * @return string
626
     */
627
    abstract public function writeInstaller();
628
629
    /**
630
     * collates the string to use as EAP outer ID
631
     * 
632
     * @return string|NULL
633
     */
634
    protected function determineOuterIdString() {
635
        $outerId = NULL;
636
        if (isset($this->attributes['internal:use_anon_outer']) && $this->attributes['internal:use_anon_outer'][0] == "1" && isset($this->attributes['internal:realm'])) {
637
            $outerId = "@" . $this->attributes['internal:realm'][0];
638
            if (isset($this->attributes['internal:anon_local_value'])) {
639
                $outerId = $this->attributes['internal:anon_local_value'][0] . $outerId;
640
            }
641
        }
642
        return $outerId;
643
    }
644
645
    /**
646
     * Array passing all options to the device module.
647
     *
648
     * $attrbutes array contains option values defined for the institution and a particular
649
     * profile (possibly overriding one another) ready for the device module to consume.
650
     * 
651
     * For each of the options the value is another array of vales (even if only one value is present).
652
     * Some attributes may be missing if they have not been configured for a viven institution or profile.
653
     *
654
     * The following attributes are meant to be used by device modules:
655
     * - <b>general:geo_coordinates</b> -  geographical coordinates of the institution or a campus
656
     * - <b>support:info_file</b>  -  consent file displayed to the users                                                         
657
     * - <b>general:logo_file</b>  -  file data containing institution logo                                                      
658
     * - <b>support:eap_types</b>  -  URL to a local support page for a specific eap methiod, not to be confused with general:url 
659
     * - <b>support:email</b>      -  email for users to contact for local instructions                                           
660
     * - <b>support:phone</b>      -  telephone number for users to contact for local instructions                                
661
     * - <b>support:url</b>        -  URL where the user will find local instructions       
662
     * - <b>internal:info_file</b> -  the pathname of the info_file saved in the working directory
663
     * - <b>internal:logo_file</b>  -  array of pathnames of logo_files saved in the working directory
664
     * - <b>internal:CAs</b> - the value is an array produced by X509::processCertificate() with the following filds
665
     * - <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.
666
     * - <b>internal:consortia</b> an array of consortion IO as declared in the config-confassistant
667
     * - <b>internal:profile_count</b> - the number of profiles for the associated IdP
668
     *
669
     *
670
     * these attributes are available and can be used, but the "internal" attributes are better suited for modules
671
     * -  eap:ca_file    -      certificate of the CA signing the RADIUS server key                                         
672
     * - <b>media:SSID</b>       -  additional SSID to configure, WPA2/AES only (device modules should use internal:SSID)
673
     * - <b>media:SSID_with_legacy</b> -  additional SSID to configure, WPA2/AES and WPA/TKIP (device modules should use internal:SSID)
674
     *
675
     * @see \core\common\X509::processCertificate()
676
     * @var array $attributes
0 ignored issues
show
Coding Style introduced by
The @var tag must be the first tag in a member variable comment
Loading history...
677
     */
678
    public $attributes;
679
680
    /**
681
     * stores the path to the module source location and is used 
682
     * by copyFile and translateFile
683
     * the only reason for it to be a public variable ies that it is set by the DeviceFactory class
684
     * module_path should not be used by module drivers.
685
     * @var string 
686
     */
687
    public $module_path;
688
689
    /**
690
     * * The optimal EAP type selected given profile and device
691
     * @var array
692
     */
693
    public $selectedEap;
694
    public $selectedEapObject;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
695
696
    /**
697
     * the path to the profile signing program
698
     * device modules which require signing should use this property to exec the signer
699
     * the signer program must accept two arguments - input and output file names
700
     * the signer program mus operate in the local directory and filenames are relative to this
701
     * directory
702
     *
703
     * @var string
704
     */
705
    public $sign;
706
    public $signer;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
707
708
    /**
709
     * The string identifier of the device (don't show this to users)
710
     * @var string
711
     */
712
    public $device_id;
713
714
    /**
715
     * See devices-template.php for a list of available options
716
     * @var array
717
     */
718
    public $options;
719
720
    /**
721
     * This string will be shown if no support email was configured by the admin
722
     * 
723
     * @var string 
724
     */
725
    public $support_email_substitute;
726
727
    /**
728
     * This string will be shown if no support URL was configured by the admin
729
     * 
730
     * @var string 
731
     */
732
    public $support_url_substitute;
733
734
    /**
735
     * This string should be used by all installer modules to set the 
736
     * installer file basename.
737
     *
738
     * @var string 
739
     */
740
    public $installerBasename;
741
742
    /**
743
     * stores the PKCS#12 DER representation of a client certificate for SilverBullet
744
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @var tag in member variable comment
Loading history...
745
    protected $clientCert;
746
747
    /**
748
     * stores identifier used by GEANTLink profiles
749
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @var tag in member variable comment
Loading history...
750
    public $deviceUUID;
751
752
}
753