Passed
Push — master ( 24c82b...10ea3d )
by Stefan
10:56 queued 34s
created

DeviceXML::setClientCetificate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 44 and the first side effect is on line 34.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 defines an abstract class used for generic XML
24
 * devices
25
 * actual modules only define available EAP types.
26
 *
27
 * @author Maja Gorecka-Wolniewicz <[email protected]>
28
 * @author Tomasz Wolniewicz <[email protected]>
29
 *
30
 * @package ModuleWriting
31
 */
32
namespace devices\xml;
33
use Exception;
34
require_once dirname(__FILE__) . '/XML.inc.php';
35
36
/**
37
 * This class implements full functionality of the generic XML device
38
 * the only fuction of the extenstions of this class is to specify
39
 * supported EAP methods.
40
 * Instead of specifying supported EAPS an extension can set $all_eaps to true
41
 * this will cause the installer to configure all EAP methods supported by 
42
 * the current profile and declared by the given device.
43
 */
44
abstract class DeviceXML extends \core\DeviceConfig {
45
46
    /**
47
     * construct the device
48
     */
49
    public function __construct() {
50
        parent::__construct();
51
    }
52
53
    /**
54
     * $langScope can be 'global' when all lang and all lang-specific information
55
     * is dumped or 'single' when only the selected lang (and defaults) are passed
56
     * NOTICE: 'global' is not yet supported
57
     * 
58
     * @var string
59
     */
60
    public $langScope;
61
    
62
    /**
63
     * whether all EAP types should be included in the file or only the 
64
     * preferred one
65
     * 
66
     * @var boolean
67
     */
68
    public $allEaps = FALSE;
69
    
70
    /**
71
     * vendor-specific additional information
72
     * 
73
     * @var array
74
     */
75
    public $VendorSpecific;
76
77
    /**
78
     * create HTML code explaining the installer
79
     * 
80
     * @return string
81
     */
82
    public function writeDeviceInfo() {
83
        \core\common\Entity::intoThePotatoes();
84
        $out = "<p>";
85
        $out .= sprintf(_("This is a generic configuration file in the IETF <a href='%s'>EAP Metadata -00</a> XML format."), "https://tools.ietf.org/html/draft-winter-opsawg-eap-metadata-00");
86
        \core\common\Entity::outOfThePotatoes();
87
        return $out;
88
    }
89
90
    /**
91
     * create the actual XML file
92
     * 
93
     * @return string filename of the generated installer
94
     * @throws Exception
95
     *
96
     */
97
    public function writeInstaller() {
98
        $attr = $this->attributes;
99
        $NAMESPACE = 'urn:RFC4282:realm';
100
//EAPIdentityProvider  begin
101
        $eapIdp = new EAPIdentityProvider();
102
        $eapIdp->setProperty('CredentialApplicability', $this->getCredentialApplicability());
103
//    $eap_idp->setProperty('ValidUntil',$this->getValidUntil());
104
// ProviderInfo->
105
        $eapIdp->setProperty('ProviderInfo', $this->getProviderInfo());
106
// TODO    $eap_idp->setProperty('VendorSpecific',$this->getVendorSpecific());
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
107
        $methodList = [];
108
        if ($this->allEaps) {
109
            $eapmethods = [];
110
            foreach ($attr['all_eaps'] as $eap) {
111
                $eapRep = $eap->getArrayRep();
112
                if (in_array($eapRep, $this->supportedEapMethods)) {
113
                    $eapmethods[] = $eapRep;
114
                }
115
            }
116
        } else {
117
            $eapmethods = [$this->selectedEap];
118
        }
119
        foreach ($eapmethods as $eap) {
120
            $methodList[] = $this->getAuthMethod($eap);
121
        }
122
        $authMethods = new AuthenticationMethods();
123
        $authMethods->setProperty('AuthenticationMethods', $methodList);
124
        $eapIdp->setProperty('AuthenticationMethods', $authMethods);
125
        if (empty($attr['internal:realm'][0])) {
126
            $eapIdp->setAttribute('ID', 'undefined');
127
            $eapIdp->setAttribute('namespace', 'urn:undefined');
128
        } else {
129
            $eapIdp->setAttribute('ID', $attr['internal:realm'][0]);
130
            $eapIdp->setAttribute('namespace', $NAMESPACE);
131
        }
132
        if ($this->langScope === 'single') {
133
            $eapIdp->setAttribute('lang', $this->languageInstance->getLang());
134
        }
135
        $eapIdp->setAttribute('version', '1');
136
137
138
// EAPIdentityProvider end
139
// Generate XML
140
141
        $rootname = 'EAPIdentityProviderList';
142
        $root = new \SimpleXMLElement("<?xml version=\"1.0\" encoding=\"utf-8\" ?><{$rootname} xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"eap-metadata.xsd\"></{$rootname}>");
143
144
        marshalObject($root, $eapIdp);
145
        $dom = dom_import_simplexml($root)->ownerDocument;
146
        //TODO schema validation makes sense so probably should be used
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
147
        if ($dom->schemaValidate(ROOT . '/devices/xml/eap-metadata.xsd') === FALSE) {
148
            throw new Exception("Schema validation failed for eap-metadata");
149
        }
150
        file_put_contents($this->installerBasename . '.eap-config', $dom->saveXML());
151
        return($this->installerBasename . '.eap-config');
152
    }
153
154
    private const ATTRIBUTENAMES = [
155
        'support:email' => 'EmailAddress',
156
        'support:url' => 'WebAddress',
157
        'support:phone' => 'Phone',
158
        'profile:description' => 'Description',
159
        'support:info_file' => 'TermsOfUse',
160
        'general:logo_file' => 'ProviderLogo',
161
    ];
162
163
    /**
164
     * 
165
     * @param string $attrName the attribute name
166
     * @return array of values for this attribute
167
     */
168
    private function getSimpleMLAttribute($attrName) {
169
        if (empty($this->attributes[$attrName][0])) {
170
            return([]);
171
        }
172
        $attributeList = $this->attributes[$attrName];
173
        if (!isset(self::ATTRIBUTENAMES[$attrName])) {
174
            $this->loggerInstance->debug(4, "Missing class definition for $attrName\n");
175
            return([]);
176
        }
177
        $className = "\devices\xml\\" . self::ATTRIBUTENAMES[$attrName];
178
        $objs = [];
179
        if ($this->langScope === 'global') {
180
            foreach ($attributeList['langs'] as $language => $value) {
181
                $language = ($language === 'C' ? 'any' : $language);
182
                $obj = new $className();
183
                $obj->setValue($value);
184
                $obj->setAttributes(['lang' => $language]);
185
                $objs[] = $obj;
186
            }
187
        } else {
188
            $obj = new $className();
189
            $obj->setValue($attributeList[0]);
190
            $objs[] = $obj;
191
        }
192
        return($objs);
193
    }
194
195
    /**
196
     * constructs the name of the institution and puts it into the XML.
197
     * consists of the best-language-match inst name, and if the inst has more 
198
     * than one profile also the best-language-match profile name
199
     * 
200
     * @return \devices\xml\DisplayName[]
201
     */
202
    private function getDisplayName() {
203
        $attr = $this->attributes;
204
        $objs = [];
205
        if ($this->langScope === 'global') {
206
            $instNameLangs = $attr['general:instname']['langs'];
207
            if ($attr['internal:profile_count'][0] > 1) {
208
                $profileNameLangs = $attr['profile:name']['langs'];
209
            }
210
            foreach ($instNameLangs as $language => $value) {
211
                $language = ($language === 'C' ? 'any' : $language);
212
                $displayname = new DisplayName();
213
                if (isset($profileNameLangs)) {
214
                    $langOrC = isset($profileNameLangs[$language]) ? $profileNameLangs[$language] : $profileNameLangs['C'];
215
                    $value .= ' - ' . $langOrC;
216
                }
217
                $displayname->setValue($value);
218
                $displayname->setAttributes(['lang' => $language]);
219
                $objs[] = $displayname;
220
            }
221
        } else {
222
            $displayname = new DisplayName();
223
            $value = $attr['general:instname'][0];
224
            if ($attr['internal:profile_count'][0] > 1) {
225
                $value .= ' - ' . $attr['profile:name'][0];
226
            }
227
            $displayname->setValue($value);
228
            $objs[] = $displayname;
229
        }
230
        return $objs;
231
    }
232
233
    /**
234
     * retrieves the provider logo and puts it into the XML structure
235
     * 
236
     * @return \devices\xml\ProviderLogo
237
     */
238
    private function getProviderLogo() {
239
        $attr = $this->attributes;
240
        if (isset($attr['general:logo_file'][0])) {
241
            $logoString = base64_encode($attr['general:logo_file'][0]);
242
            $logoMime = 'image/' . $attr['internal:logo_file'][0]['mime'];
243
            $providerlogo = new ProviderLogo();
244
            $providerlogo->setAttributes(['mime' => $logoMime, 'encoding' => 'base64']);
245
            $providerlogo->setValue($logoString);
246
            return $providerlogo;
247
        }
248
    }
249
250
    /**
251
     * retrieves provider information and puts it into the XML structure.
252
     * contains the profile description and the ToU file, if any
253
     * 
254
     * @return \devices\xml\ProviderInfo
255
     */
256
    private function getProviderInfo() {
257
        $providerinfo = new ProviderInfo();
258
        $providerinfo->setProperty('DisplayName', $this->getDisplayName());
259
        $providerinfo->setProperty('Description', $this->getSimpleMLAttribute('profile:description'));
260
        $providerinfo->setProperty('ProviderLocation', $this->getProviderLocation());
261
        $providerinfo->setProperty('ProviderLogo', $this->getProviderLogo());
262
        $providerinfo->setProperty('TermsOfUse', $this->getSimpleMLAttribute('support:info_file'));
263
        $providerinfo->setProperty('Helpdesk', $this->getHelpdesk());
264
        return $providerinfo;
265
    }
266
267
    /**
268
     * retrieves the location information and puts it into the XML structure
269
     * 
270
     * @return \devices\xml\ProviderLocation|\devices\xml\ProviderLocation[]
271
     */
272
    private function getProviderLocation() {
273
        $attr = $this->attributes;
274
        if (isset($attr['general:geo_coordinates'])) {
275
            $attrCoordinates = $attr['general:geo_coordinates'];
276
            if (count($attrCoordinates) > 1) {
277
                $location = [];
278
                foreach ($attrCoordinates as $a) {
279
                    $providerlocation = new ProviderLocation();
280
                    $b = json_decode($a, true);
281
                    $providerlocation->setProperty('Longitude', $b['lon']);
282
                    $providerlocation->setProperty('Latitude', $b['lat']);
283
                    $location[] = $providerlocation;
284
                }
285
            } else {
286
                $providerlocation = new ProviderLocation();
287
                $b = json_decode($attrCoordinates[0], true);
288
                $providerlocation->setProperty('Longitude', $b['lon']);
289
                $providerlocation->setProperty('Latitude', $b['lat']);
290
                $location = $providerlocation;
291
            }
292
            return $location;
293
        }
294
    }
295
296
    /**
297
     * retrieves helpdesk contact information and puts it into the XML structure
298
     * 
299
     * @return \devices\xml\Helpdesk
300
     */
301
    private function getHelpdesk() {
302
        $helpdesk = new Helpdesk();
303
        $helpdesk->setProperty('EmailAddress', $this->getSimpleMLAttribute('support:email'));
304
        $helpdesk->setProperty('WebAddress', $this->getSimpleMLAttribute('support:url'));
305
        $helpdesk->setProperty('Phone', $this->getSimpleMLAttribute('support:phone'));
306
        return $helpdesk;
307
    }
308
309
    /**
310
     * determine where this credential should be applicable
311
     * 
312
     * @return \devices\xml\CredentialApplicability
313
     */
314
   private function getCredentialApplicability() {
315
        $ssids = $this->attributes['internal:SSID'];
316
        $oids = $this->attributes['internal:consortia'];
317
        $credentialapplicability = new CredentialApplicability();
318
        $ieee80211s = [];
319
        foreach ($ssids as $ssid => $ciph) {
320
            $ieee80211 = new IEEE80211();
321
            $ieee80211->setProperty('SSID', $ssid);
322
            $ieee80211->setProperty('MinRSNProto', $ciph == 'AES' ? 'CCMP' : 'TKIP');
323
            $ieee80211s[] = $ieee80211;
324
        }
325
        foreach ($oids as $oid) {
326
            $ieee80211 = new IEEE80211();
327
            $ieee80211->setProperty('ConsortiumOID', $oid);
328
            $ieee80211s[] = $ieee80211;
329
        }
330
        $credentialapplicability->setProperty('IEEE80211', $ieee80211s);
331
        return $credentialapplicability;
332
    }
333
334
    /**
335
     * retrieves the parameters needed for the given EAP method and creates
336
     * appropriate nodes in the XML structure for them
337
     * 
338
     * @param array $eap the EAP type in question
339
     * @return array a recap of the findings
340
     */
341
    private function getAuthenticationMethodParams($eap) {
342
        $inner = \core\common\EAP::innerAuth($eap);
343
        $outerMethod = $eap["OUTER"];
344
345
        if (isset($inner["METHOD"]) && $inner["METHOD"]) {
346
            $innerauthmethod = new InnerAuthenticationMethod();
347
            $typeOfInner = "\devices\xml\\" . ($inner["EAP"] ? 'EAPMethod' : 'NonEAPAuthMethod');
348
            $eapmethod = new $typeOfInner();
349
            $eaptype = new Type();
350
            $eaptype->setValue($inner['METHOD']);
351
            $eapmethod->setProperty('Type', $eaptype);
352
            $innerauthmethod->setProperty($typeOfInner, $eapmethod);
353
            return ['inner_method' => $innerauthmethod, 'methodID' => $outerMethod, 'inner_methodID' => $inner['METHOD']];
354
        } else {
355
            return ['inner_method' => 0, 'methodID' => $outerMethod, 'inner_methodID' => 0];
356
        }
357
    }
358
359
    /**
360
     * sets the server-side credentials for a given EAP type
361
     * 
362
     * @param \core\common\EAP $eaptype the EAP type
363
     * @return \devices\XML\ServerSideCredential
364
     */
365
    private function setServerSideCredentials($eaptype) {
366
        $attr = $this->attributes;
367
        $serversidecredential = new ServerSideCredential();
368
// Certificates and server names
369
        $cAlist = [];
370
        $attrCaList = $attr['internal:CAs'][0];
371
        foreach ($attrCaList as $ca) {
372
            $caObject = new CA();
373
            $caObject->setValue(base64_encode($ca['der']));
374
            $caObject->setAttributes(['format' => 'X.509', 'encoding' => 'base64']);
375
            $cAlist[] = $caObject;
376
        }
377
        $serverids = [];
378
        $servers = $attr['eap:server_name'];
379
        foreach ($servers as $server) {
380
            $serverid = new ServerID();
381
            $serverid->setValue($server);
382
            $serverids[] = $serverid;
383
        }
384
        $serversidecredential->setProperty('EAPType', $eaptype->getValue());
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on core\common\EAP. ( Ignorable by Annotation )

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

384
        $serversidecredential->setProperty('EAPType', $eaptype->/** @scrutinizer ignore-call */ getValue());

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...
385
        $serversidecredential->setProperty('CA', $cAlist);
386
        $serversidecredential->setProperty('ServerID', $serverids);
387
        return $serversidecredential;
388
    }
389
    
390
    /**
391
     * sets the realm information for the client-side credential
392
     * 
393
     * @param \devices\XML\ClientSideCredential $clientsidecredential
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
394
     * @return void
395
     */
396
    private function setClientSideRealm ($clientsidecredential) {
397
        $attr = $this->attributes;
398
        $realm = \core\common\Entity::getAttributeValue($attr, 'internal:realm', 0);
399
        if ($realm === NULL) {
400
            return;
401
        }
402
        if (\core\common\Entity::getAttributeValue($attr, 'internal:verify_userinput_suffix', 0) !== 1) {
403
            return;
404
        }
405
        $clientsidecredential->setProperty('InnerIdentitySuffix', $realm);
406
        if (\core\common\Entity::getAttributeValue($attr, 'internal:hint_userinput_suffix', 0) === 1) {
407
            $clientsidecredential->setProperty('InnerIdentityHint', 'true');
408
        }
409
    }
410
    
411
    /**
412
     * sets the client certificate
413
     * 
414
     * @return \devices\XML\ClientCertificate
415
     */
416
    private function setClientCertificate() {
417
        $clientCertificateObject = new ClientCertificate();
418
        $clientCertificateObject->setValue(base64_encode($this->clientCert["certdata"]));
419
        $clientCertificateObject->setAttributes(['format' => 'PKCS12', 'encoding' => 'base64']);
420
        return $clientCertificateObject;
421
    }
422
423
    /**
424
     * sets the client-side credentials for the given EAP type
425
     * 
426
     * @param array $eapParams the EAP parameters
427
     * @return \devices\XML\ClientSideCredential
428
     */
429
    private function setClientSideCredentials($eapParams) {
430
        $clientsidecredential = new ClientSideCredential();
431
        $outerId = $this->determineOuterIdString();
432
        if ($outerId !== NULL) {
433
            $clientsidecredential->setProperty('OuterIdentity', $outerId);
434
        }
435
        $this->setClientSideRealm($clientsidecredential);
436
        $clientsidecredential->setProperty('EAPType', $eapParams['inner_methodID'] ? $eapParams['inner_methodID'] : $eapParams['methodID']);
437
                
438
        // Client Certificate
439
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
440
            $clientsidecredential->setProperty('ClientCertificate', $this->setClientCertificate());
441
        }
442
        return $clientsidecredential;
443
    }
444
    
445
    /**
446
     * sets the EAP method
447
     * 
448
     * @param \core\common\EAP $eaptype
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
449
     * @return \devices\XML\EAPMethod
450
     */
451
    private function setEapMethod($eaptype) {
452
        $eapmethod = new EAPMethod();
453
        $eapmethod->setProperty('Type', $eaptype);
454
        if (isset($this->VendorSpecific)) {
455
            $vendorspecifics = [];
456
            foreach ($this->VendorSpecific as $vs) {
457
                $vendorspecific = new VendorSpecific();
458
                $vs['value']->addAttribute('xsi:noNamespaceSchemaLocation', "xxx.xsd");
459
                $vendorspecific->setValue($vs['value']);
460
                $vendorspecific->setAttributes(['vendor' => $vs['vendor']]);
461
                $vendorspecifics[] = $vendorspecific;
462
            }
463
            $eapmethod->setProperty('VendorSpecific', $vendorspecifics);
464
        }
465
        return($eapmethod);
466
    }
467
    
468
    /**
469
     * determines the authentication method to use
470
     * 
471
     * @param \core\common\EAP $eap the EAP method
472
     * @return \devices\xml\AuthenticationMethod
473
     */
474
    private function getAuthMethod($eap) {
475
 //       $attr = $this->attributes;
476
        $authmethod = new AuthenticationMethod();
477
        $eapParams = $this->getAuthenticationMethodParams($eap);
0 ignored issues
show
Bug introduced by
$eap of type core\common\EAP is incompatible with the type array expected by parameter $eap of devices\xml\DeviceXML::g...nticationMethodParams(). ( Ignorable by Annotation )

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

477
        $eapParams = $this->getAuthenticationMethodParams(/** @scrutinizer ignore-type */ $eap);
Loading history...
478
        $eaptype = new Type();
479
        $eaptype->setValue($eapParams['methodID']);
480
// Type
481
        $authmethod->setProperty('EAPMethod', $this->setEapMethod($eaptype));
0 ignored issues
show
Bug introduced by
$eaptype of type devices\xml\Type is incompatible with the type core\common\EAP expected by parameter $eaptype of devices\xml\DeviceXML::setEapMethod(). ( Ignorable by Annotation )

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

481
        $authmethod->setProperty('EAPMethod', $this->setEapMethod(/** @scrutinizer ignore-type */ $eaptype));
Loading history...
482
483
// ServerSideCredentials
484
        $authmethod->setProperty('ServerSideCredential', $this->setServerSideCredentials($eaptype));
0 ignored issues
show
Bug introduced by
$eaptype of type devices\xml\Type is incompatible with the type core\common\EAP expected by parameter $eaptype of devices\xml\DeviceXML::setServerSideCredentials(). ( Ignorable by Annotation )

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

484
        $authmethod->setProperty('ServerSideCredential', $this->setServerSideCredentials(/** @scrutinizer ignore-type */ $eaptype));
Loading history...
485
486
// ClientSideCredentials
487
        $authmethod->setProperty('ClientSideCredential', $this->setClientSideCredentials($eapParams));
488
        
489
        if ($eapParams['inner_method']) {
490
            $authmethod->setProperty('InnerAuthenticationMethod', $eapParams['inner_method']);
491
        }
492
        return $authmethod;
493
494
495
    }
496
    
497
498
499
}
500