Test Setup Failed
Push — master ( 5548b8...63dc2d )
by Stefan
16:44
created

DeviceLinux::writeDeviceInfo()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 37
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 37
rs 8.4444
c 0
b 0
f 0
cc 8
nc 9
nop 0
1
<?php
2
/*
3
 * *****************************************************************************
4
 * Contributions to this work were made on behalf of the GÉANT project, a 
5
 * project that has received funding from the European Union’s Framework 
6
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
7
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
8
 * 691567 (GN4-1) and No. 731122 (GN4-2).
9
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
10
 * of the copyright in all material which was developed by a member of the GÉANT
11
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
12
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
13
 * UK as a branch of GÉANT Vereniging.
14
 * 
15
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
16
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
17
 *
18
 * License: see the web/copyright.inc.php file in the file structure or
19
 *          <base_url>/copyright.php after deploying the software
20
 */
21
22
/**
23
 * This file creates Linux installers
24
 *
25
 * @author Tomasz Wolniewicz <[email protected]>
26
 *
27
 * @package ModuleWriting
28
 */
29
namespace devices\linux;
30
use Exception;
31
/**
32
 * This class creates Linux installers. It supports NetworkManager and raw
33
 * wpa_supplicant files.
34
 *
35
 * @author Tomasz Wolniewicz <[email protected]>
36
 * @author Michał Gasewicz <[email protected]> (Network Manager support)
37
 *
38
 * @package ModuleWriting
39
 */
40
class DeviceLinux extends \core\DeviceConfig {
41
42
    /**
43
     * constructor. Sets supported EAP methods.
44
     */
45
    final public function __construct() {
46
        parent::__construct();
47
        $this->setSupportedEapMethods([\core\common\EAP::EAPTYPE_PEAP_MSCHAP2, \core\common\EAP::EAPTYPE_TTLS_PAP, \core\common\EAP::EAPTYPE_TTLS_MSCHAP2, \core\common\EAP::EAPTYPE_TLS, \core\common\EAP::EAPTYPE_SILVERBULLET]);
48
    }
49
50
    /**
51
     * create the actual installer script
52
     * 
53
     * @return string filename of the generated installer
54
     *
55
     */
56
    public function writeInstaller() {
57
        $installerPath = $this->installerBasename . ".py";
58
        $this->copyFile("main.py", $installerPath);
59
        $installer = fopen($installerPath,"a");
60
        if ($installer === FALSE) {
61
            throw new Exception("Unable to open installer file for writing!");
62
        }
63
        fwrite($installer, "\n\n");
64
        $this->writeMessages($installer);
65
        $this->writeConfigVars($installer);
66
        fwrite($installer, "run_installer()\n");
67
        fclose($installer);
68
        return($installerPath);
69
    }
70
    
71
    /**
72
     * produces the HTML text to be displayed when clicking on the "help" button
73
     * besides the download button.
74
     * 
75
     * @return string
76
     */
77
    public function writeDeviceInfo() {
78
        \core\common\Entity::intoThePotatoes();
79
        $ssidCount = count($this->attributes['internal:SSID']);
80
        $out = '';
81
82
        $out .= sprintf(_("The installer is in the form of a Python script. It will try to configure %s under NetworkManager and if this is either not appropriate for your system or your version of NetworkManager is too old, a wpa_supplicant config file will be created instead."), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
83
        $out .= "<p>";
84
        if ($ssidCount > 1) {
85
            if ($ssidCount > 2) {
86
                $out .= sprintf(_("In addition to <strong>%s</strong> the installer will also configure access to the following networks:"), implode(', ', CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) . " ";
87
            } else {
88
                $out .= sprintf(_("In addition to <strong>%s</strong> the installer will also configure access to:"), implode(', ', CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) . " ";
89
            }
90
            $iterator = 0;
91
            foreach ($this->attributes['internal:SSID'] as $ssid => $v) {
92
                if (!in_array($ssid, CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) {
93
                    if ($iterator > 0) {
94
                        $out .= ", ";
95
                    }
96
                    $iterator++;
97
                    $out .= "<strong>$ssid</strong>";
98
                }
99
            }
100
            $out .= "<p>";
101
        }
102
        $out .= _("The installer will create .cat_installer sub-directory in your home directory and will copy your server certificates there.");
103
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_TLS) {
104
            $out .= _("In order to connect to the network you will need a personal certificate in the form of a p12 file. You should obtain this certificate from your organisation. Consult the support page to find out how this certificate can be obtained. Such certificate files are password protected. You should have both the file and the password available during the installation process. Your p12 file will also be copied to the .cat_installer directory.");
105
        } elseif ($this->selectedEap != \core\common\EAP::EAPTYPE_SILVERBULLET) {
106
            $out .= _("In order to connect to the network you will need an account from your organisation. You should consult the support page to find out how this account can be obtained. It is very likely that your account is already activated.");
107
            $out .= "<p>";
108
            $out .= _("You will be requested to enter your account credentials during the installation. This information will be saved so that you will reconnect to the network automatically each time you are in the range.");
109
        }
110
        // nothing to say if we are doing silverbullet.
111
        $out .= "<p>";
112
        \core\common\Entity::outOfThePotatoes();
113
        return $out;
114
    }
115
    
116
    /**
117
     * writes a line of Python code into the installer script
118
     * 
119
     * @param resource $file   the file handle
120
     * @param string   $prefix prefix to write
121
     * @param string   $name   config item to write
122
     * @param string   $text   text to write
123
     * @return void
124
     */
125
    private function writeConfigLine($file, $prefix, $name, $text) {
126
        $out = $prefix . $name . ' = "' . $text;
127
        fwrite($file, wordwrap($out, 70, " \" \\\n    \"") . "\n");
128
    }
129
    
130
    /**
131
     * localises the user messages and writes them into the file
132
     * 
133
     * @param resource $file the file resource of the installer script
134
     * @return void
135
     */
136
    private function writeMessages($file) {
137
        \core\common\Entity::intoThePotatoes();
138
        $messages = [
139
        'quit'=> _("Really quit?"),
140
        'username_prompt'=> _("enter your userid"),
141
        'enter_password' => _("enter password"),
142
        'enter_import_password' => _("enter your import password"),
143
        'incorrect_password' => _("incorrect password"),
144
        'repeat_password' => _("repeat your password"),
145
        'passwords_difffer'=> _("passwords do not match"),
146
        'installation_finished' => _("Installation successful"),
147
        'cat_dir_exisits' => _("Directory {} exists; some of its files may be overwritten."),
148
        'cont' => _("Continue?"),
149
        'nm_not_supported' => _("This NetworkManager version is not supported"),
150
        'cert_error' => _("Certificate file not found, looks like a CAT error"),
151
        'unknown_version' => _("Unknown version"),
152
        'dbus_error' => _("DBus connection problem, a sudo might help"),
153
        'yes' => _("Y"),
154
        'no' => _("N"),
155
        'p12_filter' => _("personal certificate file (p12 or pfx)"),
156
        'all_filter' => _("All files"),
157
        'p12_title' => _("personal certificate file (p12 or pfx)"),
158
        'save_wpa_conf' => _("NetworkManager configuration failed, but we may generate a wpa_supplicant configuration file if you wish. Be warned that your connection password will be saved in this file as clear text."),
159
        'save_wpa_confirm' => _("Write the file"),
160
        'wrongUsernameFormat' =>_("Error: Your username must be of the form 'xxx@institutionID' e.g. '[email protected]'!"),
161
        'wrong_realm' => _("Error: your username must be in the form of 'xxx@{}'. Please enter the username in the correct format."),
162
        'wrong_realm_suffix' => _("Error: your username must be in the form of 'xxx@institutionID' and end with '{}'. Please enter the username in the correct format."),
163
        'user_cert_missing' => _("personal certificate file not found"),
164
        ];
165
        foreach ($messages as $name => $value) {
166
            $this->writeConfigLine($file, 'Messages.', $name, $value . '"');
167
        }
168
        \core\common\Entity::outOfThePotatoes();
169
    }
170
171
    /**
172
     * writes configuration variables into the installer script
173
     * 
174
     * @param resource $file the file handle
175
     * @return void
176
     */
177
    private function writeConfigVars($file) {
178
        $eapMethod = \core\common\EAP::eapDisplayName($this->selectedEap);
179
        $contacts = $this->mkSupportContacts();
180
        $tou = $this->mkUserConsent();
181
        $outerId = $this->determineOuterIdString();
182
        $config = [
183
            'instname' => $this->attributes['general:instname'][0],
184
            'profilename' => $this->attributes['profile:name'][0],
185
            'url' => $contacts['url'],
186
            'email' => $contacts['email'],
187
            'title' => "eduroam CAT",
188
            'server_match' => $this->glueServerNames(),
189
            'eap_outer' => $eapMethod['OUTER'],
190
            'eap_inner' => $eapMethod['INNER'],
191
            'init_info' => $this->mkIntro(),
192
            'init_confirmation' => $this->mkProfileConfirmation(),
193
//            'sb_user_file' => $this->mkSbUserFile(),
194
        ];
195
        
196
        $configRaw = [
197
            'ssids' => $this->mkSsidList(),
198
            'del_ssids' => $this->mkDelSsidList(),
199
            'servers' => $this->mkSubjectAltNameList(),
200
        ];
201
            
202
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_TLS && isset($this->attributes['eap-specific:tls_use_other_id']) && $this->attributes['eap-specific:tls_use_other_id'][0] == 'on') {
203
            $configRaw['use_other_tls_id'] = "True";
204
        }
205
        else {
206
            $configRaw['use_other_tls_id'] = "False";
207
        }
208
209
        if ($outerId !== NULL) {
210
            $configRaw['anonymous_identity'] = '"' . $outerId . '"';
211
        }
212
213
        if (!empty($this->attributes['internal:realm'][0])) {
214
           $config['user_realm'] = $this->attributes['internal:realm'][0];
215
        }
216
        
217
        if(!empty($this->attributes['internal:hint_userinput_suffix'][0]) && $this->attributes['internal:hint_userinput_suffix'][0] == 1) {
218
            $configRaw['hint_user_input'] = "True";
219
        }
220
        
221
        if(!empty($this->attributes['internal:verify_userinput_suffix'][0]) && $this->attributes['internal:verify_userinput_suffix'][0] == 1) {
222
            $configRaw['verify_user_realm_input'] = "True";
223
        }
224
        
225
        foreach ($config as $name => $value) {
226
            $this->writeConfigLine($file, 'Config.', $name, $value . '"');
227
        }
228
        
229
        foreach ($configRaw as $name => $value) {
230
            fwrite($file, 'Config.' . $name . ' = ' . $value . "\n");
231
        }
232
        
233
        if ($tou === '') {
234
            fwrite($file, 'Config.tou = ""' . "\n");
235
        } else {
236
            fwrite($file, 'Config.tou = """' . $tou . '"""' . "\n");
237
        }
238
        
239
        fwrite($file, 'Config.CA = """' . $this->mkCAfile() . '"""' . "\n");
240
        $sbUserFile = $this->mkSbUserFile();
241
        if ($sbUserFile !== '') {
242
            fwrite($file, 'Config.sb_user_file = """' . $sbUserFile . '"""' . "\n");
243
        }
244
    }
245
246
    /**
247
     * coerces the list of EAP server names into a single string
248
     * 
249
     * @return string
250
     */
251
    private function glueServerNames() {
252
        $serverList = $this->attributes['eap:server_name'];        
253
        if (!$serverList) {
254
            return '';
255
        }
256
        $A0 = array_reverse(explode('.', array_shift($serverList)));
257
        $B = $A0;
258
        foreach ($serverList as $oneServer) {
259
            $A = array_reverse(explode('.', $oneServer));
260
            $B = array_intersect_assoc($A0, $A);
261
            $A0 = $B;
262
        }
263
        return implode('.', array_reverse($B));
264
    }
265
266
    /**
267
     * generates the list of support contacts
268
     * 
269
     * @return array
270
     */
271
    private function mkSupportContacts() {
272
        $url = (!empty($this->attributes['support:url'][0])) ? $this->attributes['support:url'][0] : $this->support_url_substitute;
273
        $email = (!empty($this->attributes['support:email'][0])) ? $this->attributes['support:email'][0] : $this->support_email_substitute;
274
        return ['url'=>$url, 'email'=>$email];
275
    }   
276
    
277
    /**
278
     * generates the list of subjectAltNames to configure
279
     * 
280
     * @return string
281
     */
282
    private function mkSubjectAltNameList() {
283
        $serverList = $this->attributes['eap:server_name'];
284
        if (!$serverList) {
285
            return '';
286
        }
287
        $out = '';
288
        foreach ($serverList as $oneServer) {
289
            if ($out) {
290
                $out .= ', ';
291
            }
292
            $out .= "'DNS:$oneServer'";
293
        }
294
        return "[" . $out. "]";
295
    }
296
297
    /**
298
     * generates the list of SSIDs to configure
299
     * 
300
     * @return string
301
     */
302
    private function mkSsidList() {
303
        $ssids = $this->attributes['internal:SSID'];
304
        $outArray = [];
305
        foreach ($ssids as $ssid => $cipher) {
306
            $outArray[] = "'$ssid'";
307
        }
308
        return '[' . implode(', ', $outArray) . ']';
309
    }
310
    
311
    /**
312
     * generates the list of SSIDs to delete from the system
313
     * 
314
     * @return string
315
     */
316
    private function mkDelSsidList() {
317
        $outArray = [];
318
        $delSSIDs = $this->attributes['internal:remove_SSID'];
319
        foreach ($delSSIDs as $ssid => $cipher) {
320
            if ($cipher == 'DEL') {
321
                $outArray[] = "'$ssid'";
322
            }
323
        }
324
        return '[' . implode(', ', $outArray) . ']';
325
    }
326
    
327
    /**
328
     * creates a blob containing all CA certificates
329
     * 
330
     * @return string
331
     */
332
    private function mkCAfile(){
333
        $out = '';
334
        $cAlist = $this->attributes['internal:CAs'][0];
335
        foreach ($cAlist as $oneCa) {
336
            $out .= $oneCa['pem'];
337
        }
338
        return $out;
339
    }
340
    
341
    /**
342
     * generates the welcome text
343
     * 
344
     * @return string
345
     */
346
    private function mkIntro() {
347
        \core\common\Entity::intoThePotatoes();
348
        $out = _("This installer has been prepared for {0}") . '\n\n' . _("More information and comments:") . '\n\nEMAIL: {1}\nWWW: {2}\n\n' .
349
            _("Installer created with software from the GEANT project.");
350
        \core\common\Entity::outOfThePotatoes();
351
        return $out;
352
    }
353
    
354
    /**
355
     * generates text for the user consent dialog box, if any
356
     * 
357
     * @return string
358
     */
359
    private function mkUserConsent() {
360
        $out = '';
361
        if (isset($this->attributes['support:info_file'])) {
362
            if ($this->attributes['internal:info_file'][0]['mime'] == 'txt') {
363
                $out = $this->attributes['support:info_file'][0];
364
            }
365
        }
366
        return $out;
367
    }
368
    
369
    /**
370
     * generates the warning that the account will only work for inst members
371
     * 
372
     * @return string
373
     */
374
    private function mkProfileConfirmation() {
375
        \core\common\Entity::intoThePotatoes();
376
        if ($this->attributes['internal:profile_count'][0] > 1) {
377
            $out = _("This installer will only work properly if you are a member of {0} and the user group: {1}.");
378
        } else {
379
            $out = _("This installer will only work properly if you are a member of {0}.");
380
        }
381
        \core\common\Entity::outOfThePotatoes();
382
        return $out;
383
    }
384
    
385
386
    /**
387
     * generates the client certificate data for Silberbullet installers
388
     * 
389
     * @return string
390
     */
391
    private function mkSbUserFile() {
392
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
393
            return chunk_split(base64_encode($this->clientCert["certdata"]), 64, "\n");
394
        }
395
        return "";
396
    }
397
    
398
}
399