Passed
Push — master ( e870f9...8e19b6 )
by Stefan
06:30
created

DeviceXML::getSimpleMLAttribute()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 25
rs 8.9777
c 0
b 0
f 0
cc 6
nc 4
nop 1
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 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
     * $lang_scope can be 'global' wheb 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
    public $langScope;
59
    public $allEaps = FALSE;
60
    public $VendorSpecific;
61
62
    /**
63
     * create HTML code explaining the installer
64
     * 
65
     * @return string
66
     */
67
    public function writeDeviceInfo() {
68
        \core\common\Entity::intoThePotatoes();
69
        $out = "<p>";
70
        $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");
71
        \core\common\Entity::outOfThePotatoes();
72
        return $out;
73
    }
74
75
    /**
76
     * create the actual XML file
77
     * 
78
     * @return string filename of the generated installer
79
     *
80
     */
81
    public function writeInstaller() {
82
        $attr = $this->attributes;
83
        $NAMESPACE = 'urn:RFC4282:realm';
84
//EAPIdentityProvider  begin
85
        $eapIdp = new EAPIdentityProvider();
86
        $eapIdp->setProperty('CredentialApplicability', $this->getCredentialApplicability());
87
//    $eap_idp->setProperty('ValidUntil',$this->getValidUntil());
88
// ProviderInfo->
89
        $eapIdp->setProperty('ProviderInfo', $this->getProviderInfo());
90
// TODO    $eap_idp->setProperty('VendorSpecific',$this->getVendorSpecific());
91
//AuthenticationMethods
92
// TODO
93
//ID attribute
94
//lang attribute
95
        $methodList = [];
96
        if ($this->allEaps) {
97
            $eapmethods = [];
98
            foreach ($attr['all_eaps'] as $eap) {
99
                $eapRep = $eap->getArrayRep();
100
                if (in_array($eapRep, $this->supportedEapMethods)) {
101
                    $eapmethods[] = $eapRep;
102
                }
103
            }
104
        } else {
105
            $eapmethods = [$this->selectedEap];
106
        }
107
        foreach ($eapmethods as $eap) {
108
            $methodList[] = $this->getAuthMethod($eap);
109
        }
110
        $authMethods = new AuthenticationMethods();
111
        $authMethods->setProperty('AuthenticationMethods', $methodList);
112
        $eapIdp->setProperty('AuthenticationMethods', $authMethods);
113
        if (empty($attr['internal:realm'][0])) {
114
            $eapIdp->setAttribute('ID', 'undefined');
115
            $eapIdp->setAttribute('namespace', 'urn:undefined');
116
        } else {
117
            $eapIdp->setAttribute('ID', $attr['internal:realm'][0]);
118
            $eapIdp->setAttribute('namespace', $NAMESPACE);
119
        }
120
        if ($this->langScope === 'single') {
121
            $eapIdp->setAttribute('lang', $this->languageInstance->getLang());
122
        }
123
        $eapIdp->setAttribute('version', '1');
124
125
126
// EAPIdentityProvider end
127
// Generate XML
128
129
        $rootname = 'EAPIdentityProviderList';
130
        $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}>");
131
132
        marshalObject($root, $eapIdp);
133
        $dom = dom_import_simplexml($root)->ownerDocument;
134
        //TODO schema validation makes sense so probably should be used
135
        if ($dom->schemaValidate(ROOT . '/devices/xml/eap-metadata.xsd') === FALSE) {
136
            throw new Exception("Schema validation failed for eap-metadata");
137
        }
138
        file_put_contents($this->installerBasename . '.eap-config', $dom->saveXML());
139
        return($this->installerBasename . '.eap-config');
140
    }
141
142
    private $AttributeNames = [
143
        'support:email' => 'EmailAddress',
144
        'support:url' => 'WebAddress',
145
        'support:phone' => 'Phone',
146
        'profile:description' => 'Description',
147
        'support:info_file' => 'TermsOfUse',
148
        'general:logo_file' => 'ProviderLogo',
149
    ];
150
151
    /**
152
     * 
153
     * @param string $attrName the attribute name
154
     * @return array of values for this attribute
155
     */
156
    private function getSimpleMLAttribute($attrName) {
157
        if (empty($this->attributes[$attrName][0])) {
158
            return([]);
159
        }
160
        $attributeList = $this->attributes[$attrName];
161
        if (!isset($this->AttributeNames[$attrName])) {
162
            $this->loggerInstance->debug(4, "Missing class definition for $attrName\n");
163
            return([]);
164
        }
165
        $className = "\devices\xml\\" . $this->AttributeNames[$attrName];
166
        $objs = [];
167
        if ($this->langScope === 'global') {
168
            foreach ($attributeList['langs'] as $language => $value) {
169
                $language = ($language === 'C' ? 'any' : $language);
170
                $obj = new $className();
171
                $obj->setValue($value);
172
                $obj->setAttributes(['lang' => $language]);
173
                $objs[] = $obj;
174
            }
175
        } else {
176
            $obj = new $className();
177
            $obj->setValue($attributeList[0]);
178
            $objs[] = $obj;
179
        }
180
        return($objs);
181
    }
182
183
    /**
184
     * constructs the name of the institution and puts it into the XML.
185
     * consists of the best-language-match inst name, and if the inst has more 
186
     * than one profile also the best-language-match profile name
187
     * 
188
     * @return \devices\xml\DisplayName[]
189
     */
190
    private function getDisplayName() {
191
        $attr = $this->attributes;
192
        $objs = [];
193
        if ($this->langScope === 'global') {
194
            $instNameLangs = $attr['general:instname']['langs'];
195
            if ($attr['internal:profile_count'][0] > 1) {
196
                $profileNameLangs = $attr['profile:name']['langs'];
197
            }
198
            foreach ($instNameLangs as $language => $value) {
199
                $language = ($language === 'C' ? 'any' : $language);
200
                $displayname = new DisplayName();
201
                if (isset($profileNameLangs)) {
202
                    $langOrC = isset($profileNameLangs[$language]) ? $profileNameLangs[$language] : $profileNameLangs['C'];
203
                    $value .= ' - ' . $langOrC;
204
                }
205
                $displayname->setValue($value);
206
                $displayname->setAttributes(['lang' => $language]);
207
                $objs[] = $displayname;
208
            }
209
        } else {
210
            $displayname = new DisplayName();
211
            $value = $attr['general:instname'][0];
212
            if ($attr['internal:profile_count'][0] > 1) {
213
                $value .= ' - ' . $attr['profile:name'][0];
214
            }
215
            $displayname->setValue($value);
216
            $objs[] = $displayname;
217
        }
218
        return $objs;
219
    }
220
221
    /**
222
     * retrieves the provider logo and puts it into the XML structure
223
     * 
224
     * @return \devices\xml\ProviderLogo
225
     */
226
    private function getProviderLogo() {
227
        $attr = $this->attributes;
228
        if (isset($attr['general:logo_file'][0])) {
229
            $logoString = base64_encode($attr['general:logo_file'][0]);
230
            $logoMime = 'image/' . $attr['internal:logo_file'][0]['mime'];
231
            $providerlogo = new ProviderLogo();
232
            $providerlogo->setAttributes(['mime' => $logoMime, 'encoding' => 'base64']);
233
            $providerlogo->setValue($logoString);
234
            return $providerlogo;
235
        }
236
    }
237
238
    /**
239
     * retrieves provider information and puts it into the XML structure.
240
     * contains the profile description and the ToU file, if any
241
     * 
242
     * @return \devices\xml\ProviderInfo
243
     */
244
    private function getProviderInfo() {
245
        $providerinfo = new ProviderInfo();
246
        $providerinfo->setProperty('DisplayName', $this->getDisplayName());
247
        $providerinfo->setProperty('Description', $this->getSimpleMLAttribute('profile:description'));
248
        $providerinfo->setProperty('ProviderLocation', $this->getProviderLocation());
249
        $providerinfo->setProperty('ProviderLogo', $this->getProviderLogo());
250
        $providerinfo->setProperty('TermsOfUse', $this->getSimpleMLAttribute('support:info_file'));
251
        $providerinfo->setProperty('Helpdesk', $this->getHelpdesk());
252
        return $providerinfo;
253
    }
254
255
    /**
256
     * retrieves the location information and puts it into the XML structure
257
     * 
258
     * @return \devices\xml\ProviderLocation|\devices\xml\ProviderLocation[]
259
     */
260
    private function getProviderLocation() {
261
        $attr = $this->attributes;
262
        if (isset($attr['general:geo_coordinates'])) {
263
            $attrCoordinates = $attr['general:geo_coordinates'];
264
            if (count($attrCoordinates) > 1) {
265
                $location = [];
266
                foreach ($attrCoordinates as $a) {
267
                    $providerlocation = new ProviderLocation();
268
                    $b = json_decode($a, true);
269
                    $providerlocation->setProperty('Longitude', $b['lon']);
270
                    $providerlocation->setProperty('Latitude', $b['lat']);
271
                    $location[] = $providerlocation;
272
                }
273
            } else {
274
                $providerlocation = new ProviderLocation();
275
                $b = json_decode($attrCoordinates[0], true);
276
                $providerlocation->setProperty('Longitude', $b['lon']);
277
                $providerlocation->setProperty('Latitude', $b['lat']);
278
                $location = $providerlocation;
279
            }
280
            return $location;
281
        }
282
    }
283
284
    /**
285
     * retrieves helpdesk contact information and puts it into the XML structure
286
     * 
287
     * @return \devices\xml\Helpdesk
288
     */
289
    private function getHelpdesk() {
290
        $helpdesk = new Helpdesk();
291
        $helpdesk->setProperty('EmailAddress', $this->getSimpleMLAttribute('support:email'));
292
        $helpdesk->setProperty('WebAddress', $this->getSimpleMLAttribute('support:url'));
293
        $helpdesk->setProperty('Phone', $this->getSimpleMLAttribute('support:phone'));
294
        return $helpdesk;
295
    }
296
297
   private function getCredentialApplicability() {
298
        $ssids = $this->attributes['internal:SSID'];
299
        $oids = $this->attributes['internal:consortia'];
300
        $credentialapplicability = new CredentialApplicability();
301
        $ieee80211s = [];
302
        foreach ($ssids as $ssid => $ciph) {
303
            $ieee80211 = new IEEE80211();
304
            $ieee80211->setProperty('SSID', $ssid);
305
            $ieee80211->setProperty('MinRSNProto', $ciph == 'AES' ? 'CCMP' : 'TKIP');
306
            $ieee80211s[] = $ieee80211;
307
        }
308
        foreach ($oids as $oid) {
309
            $ieee80211 = new IEEE80211();
310
            $ieee80211->setProperty('ConsortiumOID', $oid);
311
            $ieee80211s[] = $ieee80211;
312
        }
313
        $credentialapplicability->setProperty('IEEE80211', $ieee80211s);
314
        return($credentialapplicability);
315
    }
316
317
    /**
318
     * retrieves the parameters needed for the given EAP method and creates
319
     * appropriate nodes in the XML structure for them
320
     * 
321
     * @param array $eap the EAP type in question
322
     * @return array a recap of the findings
323
     */
324
    private function getAuthenticationMethodParams($eap) {
325
        $inner = \core\common\EAP::innerAuth($eap);
326
        $outerMethod = $eap["OUTER"];
327
328
        if (isset($inner["METHOD"]) && $inner["METHOD"]) {
329
            $innerauthmethod = new InnerAuthenticationMethod();
330
            $typeOfInner = "\devices\xml\\" . ($inner["EAP"] ? 'EAPMethod' : 'NonEAPAuthMethod');
331
            $eapmethod = new $typeOfInner();
332
            $eaptype = new Type();
333
            $eaptype->setValue($inner['METHOD']);
334
            $eapmethod->setProperty('Type', $eaptype);
335
            $innerauthmethod->setProperty($typeOfInner, $eapmethod);
336
            return ['inner_method' => $innerauthmethod, 'methodID' => $outerMethod, 'inner_methodID' => $inner['METHOD']];
337
        } else {
338
            return ['inner_method' => 0, 'methodID' => $outerMethod, 'inner_methodID' => 0];
339
        }
340
    }
341
342
    private function setServerSideCredentials($eaptype) {
343
        $attr = $this->attributes;
344
        $serversidecredential = new ServerSideCredential();
345
// Certificates and server names
346
        $cAlist = [];
347
        $attrCaList = $attr['internal:CAs'][0];
348
        foreach ($attrCaList as $ca) {
349
            $caObject = new CA();
350
            $caObject->setValue(base64_encode($ca['der']));
351
            $caObject->setAttributes(['format' => 'X.509', 'encoding' => 'base64']);
352
            $cAlist[] = $caObject;
353
        }
354
        $serverids = [];
355
        $servers = $attr['eap:server_name'];
356
        foreach ($servers as $server) {
357
            $serverid = new ServerID();
358
            $serverid->setValue($server);
359
            $serverids[] = $serverid;
360
        }
361
        $serversidecredential->setProperty('EAPType', $eaptype->getValue());
362
        $serversidecredential->setProperty('CA', $cAlist);
363
        $serversidecredential->setProperty('ServerID', $serverids);
364
        return($serversidecredential);
365
    }
366
    
367
    private function setClientSideRealm ($clientsidecredential) {
368
        $attr = $this->attributes;
369
        $realm = \core\common\Entity::getAttributeValue($attr, 'internal:realm', 0);
370
        if ($realm === NULL) {
371
            return;
372
        }
373
        if (\core\common\Entity::getAttributeValue($attr, 'internal:verify_userinput_suffix', 0) !== 1) {
374
            return;
375
        }
376
        $clientsidecredential->setProperty('InnerIdentitySuffix', $realm);
377
        if (\core\common\Entity::getAttributeValue($attr, 'internal:hint_userinput_suffix', 0) === 1) {
378
            $clientsidecredential->setProperty('InnerIdentityHint', 'true');
379
        }
380
    }
381
    
382
    private function setClientCetificate() {
383
        $clientCertificateObject = new ClientCertificate();
384
        $clientCertificateObject->setValue(base64_encode($this->clientCert["certdata"]));
385
        $clientCertificateObject->setAttributes(['format' => 'PKCS12', 'encoding' => 'base64']);
386
        return($clientCertificateObject);
387
    }
388
389
    private function setClientSideCredentials($eapParams) {
390
        $clientsidecredential = new ClientSideCredential();
391
        $outerId = $this->determineOuterIdString();
392
        if ($outerId !== NULL) {
393
            $clientsidecredential->setProperty('OuterIdentity', $outerId);
394
        }
395
        $this->setClientSideRealm($clientsidecredential);
396
        $clientsidecredential->setProperty('EAPType', $eapParams['inner_methodID'] ? $eapParams['inner_methodID'] : $eapParams['methodID']);
397
                
398
        // Client Certificate
399
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
400
            $clientsidecredential->setProperty('ClientCertificate', $this->setClientCetificate());
401
        }
402
        return($clientsidecredential);
403
    }
404
    
405
    private function setEapMethod($eaptype) {
406
        $eapmethod = new EAPMethod();
407
        $eapmethod->setProperty('Type', $eaptype);
408
        if (isset($this->VendorSpecific)) {
409
            $vendorspecifics = [];
410
            foreach ($this->VendorSpecific as $vs) {
411
                $vendorspecific = new VendorSpecific();
412
                $vs['value']->addAttribute('xsi:noNamespaceSchemaLocation', "xxx.xsd");
413
                $vendorspecific->setValue($vs['value']);
414
                $vendorspecific->setAttributes(['vendor' => $vs['vendor']]);
415
                $vendorspecifics[] = $vendorspecific;
416
            }
417
            $eapmethod->setProperty('VendorSpecific', $vendorspecifics);
418
        }
419
        return($eapmethod);
420
    }
421
    
422
    private function getAuthMethod($eap) {
423
 //       $attr = $this->attributes;
424
        $authmethod = new AuthenticationMethod();
425
        $eapParams = $this->getAuthenticationMethodParams($eap);
426
        $eaptype = new Type();
427
        $eaptype->setValue($eapParams['methodID']);
428
// Type
429
        $authmethod->setProperty('EAPMethod', $this->setEapMethod($eaptype));
430
431
// ServerSideCredentials
432
        $authmethod->setProperty('ServerSideCredential', $this->setServerSideCredentials($eaptype));
433
434
// ClientSideCredentials
435
        $authmethod->setProperty('ClientSideCredential', $this->setClientSideCredentials($eapParams));
436
        
437
        if ($eapParams['inner_method']) {
438
            $authmethod->setProperty('InnerAuthenticationMethod', $eapParams['inner_method']);
439
        }
440
        return $authmethod;
441
442
443
    }
444
    
445
446
447
}
448