IdpList::create()   F
last analyzed

Complexity

Conditions 85
Paths 4

Size

Total Lines 397
Code Lines 242

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 7310

Importance

Changes 9
Bugs 0 Features 1
Metric Value
cc 85
eloc 242
nc 4
nop 0
dl 0
loc 397
ccs 0
cts 298
cp 0
crap 7310
rs 3.3333
c 9
b 0
f 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\Util;
6
use DOMDocument;
7
use DOMImplementation;
8
use XSLTProcessor;
9
use SimpleXMLElement;
10
11
/**
12
 * IdpList
13
 *
14
 * This class manages the list of SAML-based IdPs and their
15
 * attributes of interest. Since the InCommon-metadata.xml
16
 * file is rather large and slow to parse using xpath
17
 * queries, this class creates/reads/writes a smaller
18
 * file containing only the IdPs and the few attributes
19
 * needed by the CILogon Service.
20
 *
21
 * When you create a new instance of this class via
22
 * '$idplist = new idplist();', the code first tries to
23
 * read in a previously created idplist file. If no
24
 * such file can be read in successfully, the 'new' method
25
 * then reads in the big InCommon metadata in order to
26
 * create the class idparray and idpdom and write the
27
 * idplist file. You can (re)create the file at any time
28
 * by calling create() (which re-reads the InCommon
29
 * metadata) followed by write() (which writes the idplist
30
 * to file).
31
 *
32
 * Note that this class previously defaulted to writing the idplist
33
 * as an XML file and then reading that XML file back in. When all
34
 * InCommon and eduGAIN IdPs were allowed, the xpath queries
35
 * used on the read-in XML file were painfully slow. So now the default
36
 * is to read/write a JSON file and use a protected $idparray which is
37
 * an order of magnitude faster than xpath queries. You can still
38
 * read/write the XML file either by calling the associated read/write
39
 * methods (readXML/writeXML) or by setting $filetype='xml' in the
40
 * contructor. You will probably want to do this for the hourly cronjob
41
 * since the idplist.xml file is known to the world and should be
42
 * updated regularly.
43
 *
44
 * Example usage:
45
 *    require_once 'IdpList.php';
46
 *    // Read in extant idplist.json file, or create one from scratch
47
 *    $idplist = new IdpList();
48
 *    // Rescan InCommon metadata, update IdP list, and write to file
49
 *    $idplist->create();
50
 *    $idplist->setFilename('/tmp/newidplist.json');
51
 *    $idplist->write();
52
 */
53
class IdpList
54
{
55
    /**
56
     * @var DOMDocument $idpdom A DOMDocument which holds the list of IdP
57
     *      entityIDs and their corresponding attributes.
58
     */
59
    protected $idpdom = null;
60
61
    /**
62
     * @var mixed $idparray An array version of $idpdom. It is used
63
     * primarily since searching an array is faster than xpath query.
64
     */
65
    public $idparray = null;
66
67
    /**
68
     * @var string $idpfilename The name of the IdP list in JSON format.
69
     *      Defaults to DEFAULT_IDP_JSON.
70
     */
71
    protected $idpfilename;
72
73
    /**
74
     * @var string $incommonfilename The name of the InCommon metadata XML
75
     *      file. Defaults to DEFAULT_INCOMMON_XML.
76
     */
77
    protected $incommonfilename;
78
79
    /**
80
     * __construct
81
     *
82
     * Default constructor. This method first attempts to read in an
83
     * existing idplist from a file and store it in the idparray /
84
     * idpdom. If a valid idplist file cannot be read and
85
     * $createfile is true, neW idparray / idpdom is created and
86
     * written to file.
87
     *
88
     * @param string $idpfilename (Optional) The name of the idplist file to
89
     *        read/write. Defaults to DEFAULT_IDP_JSON.
90
     * @param string $incommonfilename (Optional) The name of the InCommon
91
     *        metadata file to read. Defaults to DEFAULT_INCOMMON_XML.
92
     * @param bool $createfile (Optional) Create idplist file if it doesn't
93
     *         exist? Defaults to true.
94
     * @param string $filetype (Optional) The type of file to read/write,
95
     *        one of 'xml' or 'json'. Defaults to 'json'.
96
     */
97
    public function __construct(
98
        $idpfilename = DEFAULT_IDP_JSON,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_IDP_JSON was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
99
        $incommonfilename = DEFAULT_INCOMMON_XML,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_INCOMMON_XML was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
100
        $createfile = true,
101
        $filetype = 'json'
102
    ) {
103
        $this->setFilename($idpfilename);
104
        $this->setInCommonFilename($incommonfilename);
105
        $result = $this->read($filetype);
106
        if (($result === false) && ($createfile)) {
107
            $this->create();
108
            $this->write($filetype);
109
        }
110
    }
111
112
    /**
113
     * read
114
     *
115
     * This reads in the idplixt file based on the input filetype.
116
     * Defaults to reading in a JSON file.
117
     *
118
     * @param string $filetype (Optional) Type type of file to read, either
119
     *        'xml' or 'json'. Defaults to 'json'.
120
     * @return bool True if the idplist was read from file. False otherwise.
121
     */
122
    public function read($filetype = 'json')
123
    {
124
        if ($filetype == 'xml') {
125
            return $this->readXML();
126
        } elseif ($filetype == 'json') {
127
            return $this->readJSON();
128
        }
129
    }
130
131
    /**
132
     * readXML
133
     *
134
     * This method attempts to read in an existing idplist XML file and
135
     * store its contents in the class $idpdom DOMDocument. It also
136
     * converts the $idpdom to the internal $idparray if not already
137
     * set.
138
     *
139
     * @return bool True if the idplist file was read in correctly.
140
     *         False otherwise.
141
     */
142
    public function readXML()
143
    {
144
        $retval = false;  // Assume read failed
145
146
        $filename = $this->getFilename();
147
        $doc = new DOMDocument();
148
        if (
149
            (is_readable($filename)) &&
150
            (($doc->load($filename, LIBXML_NOBLANKS)) !== false)
151
        ) {
152
            $this->idpdom = $doc;
153
            $this->idpdom->preserveWhiteSpace = false;
154
            $this->idpdom->formatOutput = true;
155
            // Convert the read-in DOM to idparray for later use
156
            if (is_null($this->idparray)) {
157
                $this->idparray = $this->DOM2Array($this->idpdom);
158
            }
159
            $retval = true;
160
        } else {
161
            $this->idpdom = null;
162
        }
163
164
        return $retval;
165
    }
166
167
    /**
168
     * readJSON
169
     *
170
     * This method attempts to read in an existing idplist file
171
     * (containing JSON) and store its contents in the class $idparray.
172
     * Note that this does not update the internal $idpdom.
173
     *
174
     * @return bool True if the idplist file was read in correctly.
175
     *         False otherwise.
176
     */
177
    public function readJSON()
178
    {
179
        $retval = false;  // Assume read/json_decode failed
180
181
        $filename = $this->getFilename();
182
        if (
183
            (is_readable($filename)) &&
184
            (($contents = file_get_contents($filename)) !== false) &&
185
            (($tempjson = json_decode($contents, true)) !== null)
186
        ) {
187
            $this->idparray = $tempjson;
188
            $retval = true;
189
        } else {
190
            $this->idparray = null;
191
        }
192
        return $retval;
193
    }
194
195
    /**
196
     * write
197
     *
198
     * This writes out the idplixt file based on the input filetype.
199
     * Defaults to writing a JSON file.
200
     *
201
     * @param string $filetype (Optional) Type type of file to write, either
202
     *        'xml' or 'json'. Defaults to 'json'.
203
     * @return bool True if the idplist was written to file. False
204
     *         otherwise.
205
     */
206
    public function write($filetype = 'json')
207
    {
208
        if ($filetype == 'xml') {
209
            return $this->writeXML();
210
        } elseif ($filetype == 'json') {
211
            return $this->writeJSON();
212
        }
213
    }
214
215
    /**
216
     * writeXML
217
     *
218
     * This method writes the class $idpdom to an XML file. It does
219
     * this by first writing to a temporary file in /tmp, then renaming
220
     * the temp file to the final idplist XML filename. Note that if
221
     * the internal $idpdom does not exist, it attempts to first
222
     * convert the internal $idparray to DOM and then write it.
223
     *
224
     * @return bool True if the idpdom was written to the idplist XML
225
     *         file. False otherwise.
226
     */
227
    public function writeXML()
228
    {
229
        $retval = false; // Assume write failed
230
231
        // If no idpdom, convert idparray to DOM
232
        if (is_null($this->idpdom)) {
233
            $this->idpdom = $this->array2DOM($this->idparray);
234
        }
235
236
        if (!is_null($this->idpdom)) {
237
            $this->idpdom->preserveWhiteSpace = false;
238
            $this->idpdom->formatOutput = true;
239
            $filename = $this->getFilename();
240
            $tmpfname = tempnam('/tmp', 'IDP');
241
            if (
242
                ($this->idpdom->save($tmpfname) > 0) &&
243
                (@rename($tmpfname, $filename))
244
            ) {
245
                chmod($filename, 0664);
246
                $retval = true;
247
            } else {
248
                @unlink($tmpfname);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

248
                /** @scrutinizer ignore-unhandled */ @unlink($tmpfname);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
249
            }
250
        }
251
252
        return $retval;
253
    }
254
255
    /**
256
     * writeJSON
257
     *
258
     * This method writes the class $idparray to a JSON file
259
     * It does this by first writing to a temporary file in /tmp,
260
     * then renaming the temp file to the final idplist JSON filename.
261
     *
262
     * @return bool True if the idparray was written to the idplist
263
     *         JSON file. False otherwise.
264
     */
265
    public function writeJSON()
266
    {
267
        $retval = false; // Assume write failed
268
269
        if (!is_null($this->idparray)) {
270
            $filename = $this->getFilename();
271
            $tmpfname = tempnam('/tmp', 'JSON');
272
            $json = json_encode(
273
                $this->idparray,
274
                JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES
275
            );
276
            if (
277
                ((file_put_contents($tmpfname, $json)) !== false) &&
278
                (@rename($tmpfname, $filename))
279
            ) {
280
                chmod($filename, 0664);
281
                $retval = true;
282
            } else {
283
                @unlink($tmpfname);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

283
                /** @scrutinizer ignore-unhandled */ @unlink($tmpfname);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
284
            }
285
        }
286
287
        return $retval;
288
    }
289
290
    /**
291
     * addNode
292
     *
293
     * This is a convenience method used by create() to add a new
294
     * child node (such as 'Organization_Name') to a parent idp node.
295
     * It also adds elements to the internal $idparray, thus creating
296
     * the internal idparray at the same time as the idpdom.
297
     *
298
     * @param \DOMDocument $dom A DOMDocument object
299
     * @param \DOMElement $idpnode A pointer to a parent <idp> DOMElement
300
     * @param string $nodename The name of the new child node DOMElement
301
     * @param string $nodevalue The value of the new child node DOMElement
302
     */
303
    private function addNode($dom, $idpnode, $nodename, $nodevalue)
304
    {
305
        $nodename = trim($nodename);    // Remove leading/trailing
306
        $nodevalue = trim($nodevalue);  // spaces, tabs, etc.
307
        $elemnode = $dom->createElement($nodename);
308
        $textnode = $dom->createTextNode($nodevalue);
309
        $elemnode->appendChild($textnode);
310
        $idpnode->appendChild($elemnode);
311
        // Also add element to the internal $idparray for later use
312
        $this->idparray[$idpnode->getAttribute('entityID')][$nodename] =
313
            $nodevalue;
314
    }
315
316
    /**
317
     * sortDOM
318
     *
319
     * This method is called by create() to sort the newly created
320
     * DOMDocument <idp> nodes by Display_Name. It uses an XSL
321
     * transformation to do the work. A new DOMDocument is created
322
     * and returned.
323
     *
324
     * @param DOMDocument $dom A DOMDocument to be sorted by Display_Name
325
     * @return DOMDocument A new DOMDocument with the <idp> elements sorted by
326
     *         Display_Name.
327
     */
328
    private function sortDOM($dom)
329
    {
330
        $xsltsort = <<<EOT
331
            <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
332
                            version="1.0">
333
            <xsl:output method="xml" encoding="UTF-8"/>
334
            <xsl:template match="node() | @*">
335
              <xsl:copy>
336
                <xsl:apply-templates select="node() | @*">
337
                  <xsl:sort select="translate(Display_Name,
338
                      'abcdefghijklmnopqrstuvwxyz',
339
                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"
340
                  data-type="text" order="ascending"/>
341
                </xsl:apply-templates>
342
              </xsl:copy>
343
            </xsl:template>
344
            </xsl:stylesheet>
345
EOT;
346
        $xsl = new DOMDocument('1.0');
347
        $xsl->loadXML($xsltsort);
348
        $proc = new XSLTProcessor();
349
        $proc->importStyleSheet($xsl);
350
        $newdom = $proc->transformToDoc($dom);
351
        return $newdom;
352
    }
353
354
    /**
355
     * create
356
     *
357
     * This method is used to populate the class $idpdom DOMDocument
358
     * using information from the InCommon metadata file. Note that
359
     * method updates $idpdom and $idparray. If you want to save either
360
     * to a file, be sure to call write() afterwards.
361
     *
362
     * @return bool True upon successful extraction of IdP information
363
     *         from the InCommon metadata file into the class
364
     *         $idpdom DOMDocument. False otherwise.
365
     */
366
    public function create()
367
    {
368
        $retval = false; // Assume create failed
369
        if (is_readable($this->getInCommonFilename())) {
370
            // Read in the InCommon metadata file
371
            $xmlstr = @file_get_contents($this->getInCommonFilename());
372
            if (strlen($xmlstr) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $xmlstr can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

372
            if (strlen(/** @scrutinizer ignore-type */ $xmlstr) > 0) {
Loading history...
373
                // Need to fix the namespace for Xpath queries to work
374
                $xmlstr = str_replace('xmlns=', 'ns=', $xmlstr);
375
                $xml = new SimpleXMLElement($xmlstr);
376
377
                // Select only IdPs from the InCommon metadata
378
                $result = $xml->xpath(
379
                    "//EntityDescriptor/IDPSSODescriptor" .
380
                    "/ancestor::EntityDescriptor"
381
                );
382
383
                // Create a DOMDocument to build up the list of IdPs.
384
                $domi = new DOMImplementation();
385
                $dom = $domi->createDocument(null, 'idps');
386
                $idps = $dom->documentElement; // Top level <idps> element
387
388
                // Loop through the IdPs searching for desired attributes
389
                foreach ($result as $idx) {
390
                    // Need to set namespace prefixes for xpath queries to work
391
                    $sxe = $idx[0];
392
                    $sxe->registerXPathNamespace(
393
                        'mdattr',
394
                        'urn:oasis:names:tc:SAML:metadata:attribute'
395
                    );
396
                    $sxe->registerXPathNamespace(
397
                        'saml',
398
                        'urn:oasis:names:tc:SAML:2.0:assertion'
399
                    );
400
                    $sxe->registerXPathNamespace(
401
                        'mdrpi',
402
                        'urn:oasis:names:tc:SAML:metadata:rpi'
403
                    );
404
                    $sxe->registerXPathNamespace(
405
                        'mdui',
406
                        'urn:oasis:names:tc:SAML:metadata:ui'
407
                    );
408
409
                    // Skip any hide-from-discovery entries
410
                    $xp = $sxe->xpath(
411
                        "Extensions/mdattr:EntityAttributes/" .
412
                        "saml:Attribute[@Name='" .
413
                        "http://macedir.org/entity-category']/" .
414
                        "saml:AttributeValue"
415
                    );
416
                    if (($xp !== false) && (count($xp) > 0)) {
417
                        $hide = false;
418
                        foreach ($xp as $value) {
419
                            if ($value == 'http://refeds.org/category/hide-from-discovery') {
420
                                $hide = true;
421
                                break;
422
                            }
423
                        }
424
                        if ($hide) {
425
                            continue;
426
                        }
427
                    }
428
429
                    // Get the entityID of the IdP. Save it for later.
430
                    // The entityID will be the keys of the class idpdom.
431
                    $entityID = '';
432
                    $xp = $idx[0]->xpath('attribute::entityID');
433
                    if (($xp !== false) && (count($xp) > 0)) {
434
                        $entityID = (string)$xp[0]->entityID;
435
                    } else { // No entityID is bad!
436
                        continue;
437
                    }
438
439
                    // CIL-741 Omit IdPs in the global REDLIT_IDP_ARRAY
440
                    if (in_array($entityID, REDLIT_IDP_ARRAY)) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\REDLIT_IDP_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
441
                        continue;
442
                    }
443
444
                    // Create an <idp> element to hold sub elements
445
                    $idp = $dom->createElement('idp');
446
                    $idp->setAttribute('entityID', $entityID);
447
                    $idps->appendChild($idp);
448
449
                    // Search for the desired <idp> attribute sub-blocks
450
451
                    // Look for OrganizationDisplayName and mdui:DisplayName.
452
                    $Organization_Name = '';
453
                    $Display_Name = '';
454
455
                    $xp = $idx[0]->xpath(
456
                        "Organization/OrganizationDisplayName[starts-with(@xml:lang,'en')]"
457
                    );
458
                    if (($xp !== false) && (count($xp) > 0)) {
459
                        $Organization_Name = (string)$xp[0];
460
                    }
461
462
                    $xp = $sxe->xpath(
463
                        "IDPSSODescriptor/Extensions/mdui:UIInfo/mdui:DisplayName[starts-with(@xml:lang,'en')]"
464
                    );
465
                    if (($xp !== false) && (count($xp) > 0)) {
466
                        $Display_Name = (string)$xp[0];
467
                    }
468
469
                    // If neither OrganizationDisplayName nor mdui:DisplayName
470
                    // was found, then use the entityID as a last resort.
471
                    if (
472
                        (strlen($Organization_Name) == 0) &&
473
                        (strlen($Display_Name) == 0)
474
                    ) {
475
                        $Organization_Name = $entityID;
476
                        $Display_Name = $entityID;
477
                    }
478
479
                    // Add nodes for both Organization_Name and Display_Name,
480
                    // using the value of the other if one is empty.
481
                    $this->addNode(
482
                        $dom,
483
                        $idp,
484
                        'Organization_Name',
485
                        ((strlen($Organization_Name) > 0) ?
486
                            $Organization_Name :
487
                            $Display_Name)
488
                    );
489
                    $this->addNode(
490
                        $dom,
491
                        $idp,
492
                        'Display_Name',
493
                        ((strlen($Display_Name) > 0) ?
494
                            $Display_Name :
495
                            $Organization_Name)
496
                    );
497
498
                    $xp = $idx[0]->xpath('Organization/OrganizationURL');
499
                    if (($xp !== false) && (count($xp) > 0)) {
500
                        $this->addNode($dom, $idp, 'Home_Page', (string)$xp[0]);
501
                    }
502
503
                    $name = '';
504
                    $xp = $idx[0]->xpath(
505
                        "ContactPerson[@contactType='support']/GivenName"
506
                    );
507
                    if (($xp !== false) && (count($xp) > 0)) {
508
                        $name = (string)$xp[0];
509
                    }
510
                    $xp = $idx[0]->xpath(
511
                        "ContactPerson[@contactType='support']/SurName"
512
                    );
513
                    if (($xp !== false) && (count($xp) > 0)) {
514
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
515
                            (string)($xp[0]);
516
                    }
517
                    if (strlen($name) > 0) {
518
                        $this->addNode($dom, $idp, 'Support_Name', $name);
519
                    }
520
521
                    $xp = $idx[0]->xpath(
522
                        "ContactPerson[@contactType='support']/EmailAddress"
523
                    );
524
                    if (($xp !== false) && (count($xp) > 0)) {
525
                        $this->addNode(
526
                            $dom,
527
                            $idp,
528
                            'Support_Address',
529
                            (string)$xp[0]
530
                        );
531
                    }
532
533
                    $name = '';
534
                    $xp = $idx[0]->xpath(
535
                        "ContactPerson[@contactType='technical']/GivenName"
536
                    );
537
                    if (($xp !== false) && (count($xp) > 0)) {
538
                        $name = (string)$xp[0];
539
                    }
540
                    $xp = $idx[0]->xpath(
541
                        "ContactPerson[@contactType='technical']/SurName"
542
                    );
543
                    if (($xp !== false) && (count($xp) > 0)) {
544
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
545
                            (string)($xp[0]);
546
                    }
547
                    if (strlen($name) > 0) {
548
                        $this->addNode($dom, $idp, 'Technical_Name', $name);
549
                    }
550
551
                    $xp = $idx[0]->xpath(
552
                        "ContactPerson[@contactType='technical']/EmailAddress"
553
                    );
554
                    if (($xp !== false) && (count($xp) > 0)) {
555
                        $this->addNode(
556
                            $dom,
557
                            $idp,
558
                            'Technical_Address',
559
                            (string)$xp[0]
560
                        );
561
                    }
562
563
                    $name = '';
564
                    $xp = $idx[0]->xpath(
565
                        "ContactPerson[@contactType='administrative']/GivenName"
566
                    );
567
                    if (($xp !== false) && (count($xp) > 0)) {
568
                        $name = (string)$xp[0];
569
                    }
570
                    $xp = $idx[0]->xpath(
571
                        "ContactPerson[@contactType='administrative']/SurName"
572
                    );
573
                    if (($xp !== false) && (count($xp) > 0)) {
574
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
575
                            (string)($xp[0]);
576
                    }
577
                    if (strlen($name) > 0) {
578
                        $this->addNode($dom, $idp, 'Administrative_Name', $name);
579
                    }
580
581
                    $xp = $idx[0]->xpath(
582
                        "ContactPerson[@contactType='administrative']/EmailAddress"
583
                    );
584
                    if (($xp !== false) && (count($xp) > 0)) {
585
                        $this->addNode(
586
                            $dom,
587
                            $idp,
588
                            'Administrative_Address',
589
                            (string)$xp[0]
590
                        );
591
                    }
592
593
                    // Check for assurance-certification = silver, bronze, or SIRTFI
594
                    $xp = $sxe->xpath(
595
                        "Extensions/mdattr:EntityAttributes/" .
596
                        "saml:Attribute[@Name='" .
597
                        "urn:oasis:names:tc:SAML:attribute:" .
598
                        "assurance-certification']/saml:AttributeValue"
599
                    );
600
                    if (($xp !== false) && (count($xp) > 0)) {
601
                        foreach ($xp as $value) {
602
                            if ($value == 'http://id.incommon.org/assurance/silver') {
603
                                $this->addNode($dom, $idp, 'Silver', '1');
604
                            } elseif ($value == 'http://id.incommon.org/assurance/bronze') {
605
                                $this->addNode($dom, $idp, 'Bronze', '1');
606
                            } elseif ($value == 'https://refeds.org/sirtfi') {
607
                                $this->addNode($dom, $idp, 'SIRTFI', '1');
608
                            }
609
                        }
610
                    }
611
612
                    // Check for registered-by-incommon
613
                    $xp = $sxe->xpath(
614
                        "Extensions/mdattr:EntityAttributes/" .
615
                        "saml:Attribute[@Name='" .
616
                        "http://macedir.org/entity-category']/" .
617
                        "saml:AttributeValue"
618
                    );
619
                    if (($xp !== false) && (count($xp) > 0)) {
620
                        foreach ($xp as $value) {
621
                            if ($value == 'http://id.incommon.org/category/registered-by-incommon') {
622
                                $this->addNode(
623
                                    $dom,
624
                                    $idp,
625
                                    'Registered_By_InCommon',
626
                                    '1'
627
                                );
628
                                break;
629
                            }
630
                        }
631
                    }
632
633
                    // Check for research-and-scholarship
634
                    $xp = $sxe->xpath(
635
                        "Extensions/mdattr:EntityAttributes/" .
636
                        "saml:Attribute[@Name='" .
637
                        "http://macedir.org/entity-category-support']/" .
638
                        "saml:AttributeValue"
639
                    );
640
                    if (($xp !== false) && (count($xp) > 0)) {
641
                        $addedrands = false;
642
                        $incommonrands = false;
643
                        $refedsrands = false;
644
                        foreach ($xp as $value) {
645
                            if ($value == 'http://id.incommon.org/category/research-and-scholarship') {
646
                                $incommonrands = true;
647
                                $this->addNode($dom, $idp, 'InCommon_RandS', '1');
648
                            }
649
                            if ($value == 'http://refeds.org/category/research-and-scholarship') {
650
                                $refedsrands = true;
651
                                $this->addNode($dom, $idp, 'REFEDS_RandS', '1');
652
                            }
653
                            if (
654
                                (!$addedrands) &&
655
                                ($incommonrands || $refedsrands)
656
                            ) {
657
                                $addedrands = true;
658
                                $this->addNode($dom, $idp, 'RandS', '1');
659
                            }
660
                        }
661
                    }
662
663
                    // CIL-558 Check for <SingleLogoutService>
664
                    $Logout = '';
665
                    // First, check for HTTP-Redirect version
666
                    $xp = $sxe->xpath("IDPSSODescriptor/SingleLogoutService" .
667
                        "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect']");
668
                    if (($xp !== false) && (count($xp) > 0)) {
669
                        $Logout = (string)($xp[0]->attributes())['Location'];
670
                    }
671
                    // If no HTTP-Redirect, check for HTTP-POST
672
                    if (empty($Logout)) {
673
                        $xp = $sxe->xpath("IDPSSODescriptor/SingleLogoutService" .
674
                            "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST']");
675
                        if (($xp !== false) && (count($xp) > 0)) {
676
                            $Logout = (string)($xp[0]->attributes())['Location'];
677
                        }
678
                    }
679
                    // Finally, a hack for Shibboleth-based IdPs.
680
                    // Check for <SingleSignOnService> HTTP-Redirect
681
                    // and regex for the built-in Simple Logout URL.
682
                    if (empty($Logout)) {
683
                        $xp = $sxe->xpath("IDPSSODescriptor/SingleSignOnService" .
684
                            "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect']");
685
                        if (($xp !== false) && (count($xp) > 0)) {
686
                            $tmp = (string)($xp[0]->attributes())['Location'];
687
                            if (preg_match('|^(.*)/profile/|', $tmp, $matches)) {
688
                                $Logout = $matches[1] . '/profile/Logout';
689
                            }
690
                        }
691
                    }
692
                    if (!empty($Logout)) {
693
                        // If Shib IdP, transform URL into Simple Logout URL
694
                        // https://wiki.shibboleth.net/confluence/x/AAJSAQ
695
                        if (preg_match('|^(.*)/profile/|', $Logout, $matches)) {
696
                            $Logout = $matches[1] . '/profile/Logout';
697
                        }
698
                        $this->addNode($dom, $idp, 'Logout', $Logout);
699
                    }
700
                }
701
702
                // Read in any test IdPs and add them to the list
703
                $doc = new DOMDocument();
704
                if (
705
                    (defined('TEST_IDP_XML')) &&
706
                    (!empty(TEST_IDP_XML)) &&
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\TEST_IDP_XML was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
707
                    (is_readable(TEST_IDP_XML)) &&
708
                    ((@$doc->load(TEST_IDP_XML)) !== false)
709
                ) {
710
                    $idpnodes = $doc->getElementsByTagName('idp');
711
                    foreach ($idpnodes as $idpnode) {
712
                        // Check if the entityID already exists. If so,
713
                        // delete it from both the idps DOM and the idparray
714
                        // and instead add the one from the testidplist.
715
                        $entityID = $idpnode->attributes->item(0)->value;
716
                        if (array_key_exists($entityID, $this->idparray)) {
717
                            // Easy - simply delete the array entry for the
718
                            // existing entityID
719
                            unset($this->idparray[$entityID]);
720
721
                            // Hard - search through the current DOM for a
722
                            // matching entityID to get the DOMNode, which
723
                            // can then be removed from the DOM.
724
                            $curridpnodes = $dom->getElementsByTagName('idp');
725
                            foreach ($curridpnodes as $curridpnode) {
726
                                $currEntityID =
727
                                    $curridpnode->attributes->item(0)->value;
728
                                if ($currEntityID == $entityID) {
729
                                    $idps->removeChild($curridpnode);
730
                                    break;
731
                                }
732
                            }
733
                        }
734
735
                        // Add the new idp node to the DOM
736
                        $node = $dom->importNode($idpnode, true);
737
                        $idps->appendChild($node);
738
739
                        // Add the testidplist nodes to the $idparray
740
                        foreach ($node->childNodes as $child) {
741
                            if ($child->nodeName != '#text') {
742
                                $this->idparray[$entityID][$child->nodeName] =
743
                                    $child->nodeValue;
744
                            }
745
                        }
746
                    }
747
                }
748
749
                // Sort the DOMDocument and idparray by Display_Name
750
                $this->idpdom = $this->sortDOM($dom);
751
                uasort($this->idparray, function ($a, $b) {
752
                    return strcasecmp(
753
                        $a['Display_Name'],
754
                        $b['Display_Name']
755
                    );
756
                });
757
758
                $retval = true;
759
            }
760
        }
761
762
        return $retval;
763
    }
764
765
    /**
766
     * getFilename
767
     *
768
     * This function returns a string of the full path of the IdP list
769
     * filename.  See also setFilename().
770
     *
771
     * @return string The IdP list filename.
772
     */
773
    public function getFilename()
774
    {
775
        return $this->idpfilename;
776
    }
777
778
    /**
779
     * setFilename
780
     *
781
     * This function sets the string of the full path of the IdP list
782
     * filename.  See also getFilename().
783
     *
784
     * @param string $filename he new name of the IdP list filename.
785
     */
786
    public function setFilename($filename)
787
    {
788
        $this->idpfilename = $filename;
789
    }
790
791
    /**
792
     * getInCommonFilename
793
     *
794
     * This function returns a string of the full path of the InCommon
795
     * metadata filename.  See also setInCommonFilename().
796
     *
797
     * @return string The InCommon metadata filename.
798
     */
799
    public function getInCommonFilename()
800
    {
801
        return $this->incommonfilename;
802
    }
803
804
    /**
805
     * setInCommonFilename
806
     *
807
     * This function sets the string of the full path of the InCommon
808
     * metadata filename.  See also getInCommonFilename().
809
     *
810
     * @param string $filename The new name of the InCommon metadata filename.
811
     */
812
    public function setInCommonFilename($filename)
813
    {
814
        $this->incommonfilename = $filename;
815
    }
816
817
    /**
818
     * getEntityIDs
819
     *
820
     * This method returns the entityIDs of the idplist as an array.
821
     *
822
     * @return array An array of the entityIDs
823
     */
824
    public function getEntityIDs()
825
    {
826
        $retarr = array();
827
        if (is_array($this->idparray)) {
828
            $retarr = array_keys($this->idparray);
829
        }
830
        return $retarr;
831
    }
832
833
    /**
834
     * getOrganizationName
835
     *
836
     * This function returns the Organization_Name of the selected
837
     * $entityID.
838
     *
839
     * @param string $entityID The entityID to search for
840
     * @return string The Organization_Name for the $entityID. Return
841
     *         string is empty if no matching $entityID found.
842
     */
843
    public function getOrganizationName($entityID)
844
    {
845
        $retval = '';
846
847
        if (
848
            ($this->exists($entityID)) &&
849
            (isset($this->idparray[$entityID]['Organization_Name']))
850
        ) {
851
            $retval = $this->idparray[$entityID]['Organization_Name'];
852
        }
853
        return $retval;
854
    }
855
856
    /**
857
     * getDisplayName
858
     *
859
     * This function returns the Display_Name of the selected
860
     * $entityID.
861
     *
862
     * @param string $entityID The entityID to search for
863
     * @return string The Display_Name for the $entityID. Return
864
     *         string is empty if no matching $entityID found.
865
     */
866
    public function getDisplayName($entityID)
867
    {
868
        $retval = '';
869
        if (
870
            ($this->exists($entityID)) &&
871
            (isset($this->idparray[$entityID]['Display_Name']))
872
        ) {
873
            $retval = $this->idparray[$entityID]['Display_Name'];
874
        }
875
        return $retval;
876
    }
877
878
    /**
879
     * getLogout
880
     *
881
     * This function returns the Logout URL of the selected $entityID.
882
     *
883
     * @param string $entityID The entityID to search for
884
     * @return string The Logout  URLfor the $entityID. Return
885
     *         string is empty if no matching $entityID found.
886
     */
887
    public function getLogout($entityID)
888
    {
889
        $retval = '';
890
        if (
891
            ($this->exists($entityID)) &&
892
            (isset($this->idparray[$entityID]['Logout']))
893
        ) {
894
            $retval = $this->idparray[$entityID]['Logout'];
895
        }
896
        return $retval;
897
    }
898
899
    /**
900
     * entityIDExists
901
     *
902
     * This function searchs for the given idp entityID.
903
     *
904
     * @param string $entityID The entityID to search for
905
     * @return bool True if the given entityID is found. False otherwise.
906
     */
907
    public function entityIDExists($entityID)
908
    {
909
        return (isset($this->idparray[$entityID]));
910
    }
911
912
    /**
913
     * exists
914
     *
915
     * This is simply a convenience function for entityIDExists.
916
     *
917
     * @param string $entityID The enityID to search for
918
     * @return bool True if the given entityID is found. False otherwise.
919
     */
920
    public function exists($entityID)
921
    {
922
        return $this->entityIDExists($entityID);
923
    }
924
925
    /**
926
     * isAttributeSet
927
     *
928
     * This function checks if the passed-in $attr is set to '1' for
929
     * the entityID, and returns true if so.
930
     *
931
     * @param string $entityID The enityID to search for.
932
     * @param string $attr The attribute in question.
933
     * @return bool True if the given attribute is '1' for the entityID.
934
     *         False otherwise.
935
     */
936
    public function isAttributeSet($entityID, $attr)
937
    {
938
        return (
939
            ($this->exists($entityID)) &&
940
            (isset($this->idparray[$entityID][$attr])) &&
941
            ($this->idparray[$entityID][$attr] == 1)
942
        );
943
    }
944
945
    /**
946
     * isSilver
947
     *
948
     * This method searches for the given entityID and checks if the
949
     *'Silver' entry has been set to '1'.
950
     *
951
     * @param string $entityID The enityID to search for
952
     * @return bool True if the given entityID is certified 'Silver'.
953
     *         False otherwise.
954
     */
955
    public function isSilver($entityID)
956
    {
957
        return $this->isAttributeSet($entityID, 'Silver');
958
    }
959
960
    /**
961
     * isBronze
962
     *
963
     * This method searches for the given entityID and checks if the
964
     *'Bronze' entry has been set to '1'.
965
     *
966
     * @param string $entityID The enityID to search for
967
     * @return bool True if the given entityID is certified 'Bronze'.
968
     *         False otherwise.
969
     */
970
    public function isBronze($entityID)
971
    {
972
        return $this->isAttributeSet($entityID, 'Bronze');
973
    }
974
975
    /**
976
     * isRandS
977
     *
978
     * This method searches for the given entityID and checks if the
979
     *'RandS' entry has been set to '1'.
980
     *
981
     * @param string $entityID The enityID to search for
982
     * @return bool True if the given entityID is listed as 'RandS'
983
     *         (research-and-scholarship). False otherwise.
984
     */
985
    public function isRandS($entityID)
986
    {
987
        return $this->isAttributeSet($entityID, 'RandS');
988
    }
989
990
    /**
991
     * isInCommonRandS
992
     *
993
     * This method searches for the given entityID and checks if the
994
     *'InCommon_RandS' entry has been set to '1'.
995
     *
996
     * @param string $entityID The enityID to search for
997
     * @return bool True if the given entityID is listed as
998
     *        'InCommon_RandS'. False otherwise.
999
     */
1000
    public function isInCommonRandS($entityID)
1001
    {
1002
        return $this->isAttributeSet($entityID, 'InCommon_RandS');
1003
    }
1004
1005
    /**
1006
     * isREFEDSRandS
1007
     *
1008
     * This method searches for the given entityID and checks if the
1009
     *'REFEDS_RandS' entry has been set to '1'.
1010
     *
1011
     * @param string $entityID The enityID to search for
1012
     * @return bool True if the given entityID is listed as
1013
     *         'REFEDS_RandS'. False otherwise.
1014
     */
1015
    public function isREFEDSRandS($entityID)
1016
    {
1017
        return $this->isAttributeSet($entityID, 'REFEDS_RandS');
1018
    }
1019
1020
    /**
1021
     * isRegisteredByInCommon
1022
     *
1023
     * This method searches for the given entityID and checks if the
1024
     *'Registered_By_InCommon' entry has been set to '1'.
1025
     *
1026
     * @param string $entityID The enityID to search for
1027
     * @return bool True if the given entityID is listed as
1028
     *         'Registered_By_InCommon'. False otherwise.
1029
     */
1030
    public function isRegisteredByInCommon($entityID)
1031
    {
1032
        return $this->isAttributeSet($entityID, 'Registered_By_InCommon');
1033
    }
1034
1035
    /**
1036
     * isSIRTFI
1037
     *
1038
     * This method searches for the given entityID and checks if the
1039
     *'SIRTFI' entry has been set to '1'.
1040
     *
1041
     * @param string $entityID The enityID to search for
1042
     * @return bool True if the given entityID is listed as
1043
     *         SIRTFI. False otherwise.
1044
     */
1045
    public function isSIRTFI($entityID)
1046
    {
1047
        return $this->isAttributeSet($entityID, 'SIRTFI');
1048
    }
1049
1050
    /**
1051
     * isOauth2
1052
     *
1053
     * This method returns true if the passed-in 'entitID' is one of the
1054
     * supported OAuth2 Identity Providers. Otherwise, false.
1055
     *
1056
     * @param string $entityID The enityID to search for
1057
     * @return bool True if the given entityID is an OAuth2 IdP.
1058
     *         False otherwise.
1059
     */
1060
    public function isOAuth2($entityID)
1061
    {
1062
        $retval = false;
1063
        if (
1064
            ($entityID == Util::getAuthzUrl('Google')) ||
1065
            ($entityID == Util::getAuthzUrl('GitHub')) ||
1066
            ($entityID == Util::getAuthzUrl('ORCID'))
1067
        ) {
1068
            $retval = true;
1069
        }
1070
        return $retval;
1071
    }
1072
1073
    /**
1074
     * getSAMLIdPs
1075
     *
1076
     * This method returns a two-dimensional array of SAML-based IdPs.
1077
     * The primary key of the array is the entityID, the secondary key is
1078
     * either 'Organization_Name' (corresponds to OrganizationDisplayName)
1079
     * or 'Display_Name' (corresponds to mdui:DisplayName).
1080
     * If a non-null parameter is passed in it returns a subset of the
1081
     * InCommon IdPs. 2 means list only R&S IdPs, 3 means list only IdPs
1082
     * marked as Registered By InCommon.
1083
     *
1084
     * @param int $filter
1085
     *        null => all SAML-based IdPs
1086
     *        2    => R&S SAML-based IdPs
1087
     *        3    => "Registered By InCommon" IdPs
1088
     * $return array An array of SAML-based IdP Organization Names and Display
1089
     *         Names, possibly filtered R&S / Registered By InCommon.
1090
     */
1091
    public function getSAMLIdPs($filter = null)
1092
    {
1093
        $retarr = array();
1094
1095
        foreach ($this->idparray as $key => $value) {
1096
            if (
1097
                (!is_null($filter)) &&
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! is_null($filter) && $...isteredByInCommon($key), Probably Intended Meaning: ! is_null($filter) && ($...steredByInCommon($key))
Loading history...
1098
                (($filter === 2) &&
1099
                 (!$this->isRandS($key))) ||
1100
                (($filter === 3) &&
1101
                 (!$this->isRegisteredByInCommon($key)))
1102
            ) {
1103
                continue;
1104
            }
1105
            if (
1106
                ($this->exists($key)) &&
1107
                (isset($this->idparray[$key]['Organization_Name']))
1108
            ) {
1109
                $retarr[$key]['Organization_Name'] = $this->idparray[$key]['Organization_Name'];
1110
            }
1111
            if (
1112
                ($this->exists($key)) &&
1113
                (isset($this->idparray[$key]['Display_Name']))
1114
            ) {
1115
                $retarr[$key]['Display_Name'] = $this->idparray[$key]['Display_Name'];
1116
            }
1117
        }
1118
1119
        return $retarr;
1120
    }
1121
1122
    /**
1123
     * getRandSIdPs
1124
     *
1125
     * This method returns an array of R&S IdPs where the keys
1126
     * of the array are the entityIDs and the values are the
1127
     * pretty print Organization Names.
1128
     *
1129
     * @return array An array of Research and Scholarship (R&S) IdPs.
1130
     */
1131
    public function getRandSIdPs()
1132
    {
1133
        return $this->getSAMLIdPs(2);
1134
    }
1135
1136
    /**
1137
     * getRegisteredByInCommonIdPs
1138
     *
1139
     * This method returns an array of IdPs that have been tagged as
1140
     * "Registered_By_InCommon". The keys of the array are the entityIDs
1141
     * and the values are the pretty print Organization Names.
1142
     *
1143
     * @return array An array of Research and Scholarship (R&S) IdPs.
1144
     */
1145
    public function getRegisteredByInCommonIdPs()
1146
    {
1147
        return $this->getSAMLIdPs(3);
1148
    }
1149
1150
    /**
1151
     * getShibInfo
1152
     *
1153
     * This function returns an array with two types of Shibboleth
1154
     * information.  The first set of info is specific to the user's
1155
     * current Shibboleth session, such as REMOTE_USER. The second set
1156
     * of info reads info from the passed-in metadata file specific to
1157
     * the IdP, such as the pretty-print name of the IdP.
1158
     *
1159
     * @param string $entityID (Optional) The entityID to search for in
1160
     *        the InCommon metadata. Defaults to the HTTP header
1161
     *        HTTP_SHIB_IDENTITY_PROVIDER.
1162
     * @return array  An array containing the various shibboleth
1163
     *         attributes for the current Shibboleth session. The
1164
     *         keys of the array are 'pretty print' names of the
1165
     *         various attribute value names (such as
1166
     *         'User Identifier' for REMOTE_USER) and the values
1167
     *         of the array are the actual Shibboleth session values.
1168
     */
1169
    public function getShibInfo($entityID = '')
1170
    {
1171
        $shibarray = array();  // Array to be returned
1172
1173
        // Set the blob set of info, namely those shib attributes which
1174
        // were given by the IdP when the user authenticated.
1175
        if (strlen($entityID) == 0) {
1176
            $entityID = Util::getServerVar('HTTP_SHIB_IDENTITY_PROVIDER');
1177
        }
1178
        // CIL-254 - For LIGO backup IdPs, remap entityID to the main IdP
1179
        if (
1180
            preg_match(
1181
                '%(https://login)[^\.]*(.ligo.org/idp/shibboleth)%',
1182
                $entityID,
1183
                $matches
1184
            )
1185
        ) {
1186
            $entityID = $matches[1] . $matches[2];
1187
        }
1188
        $shibarray['Identity Provider'] = $entityID;
1189
        $shibarray['User Identifier'] = Util::getServerVar('REMOTE_USER');
1190
        $shibarray['ePPN'] = Util::getServerVar('HTTP_EPPN');
1191
        $shibarray['ePTID'] = Util::getServerVar('HTTP_PERSISTENT_ID');
1192
        $shibarray['First Name'] = Util::getServerVar('HTTP_GIVENNAME');
1193
        $shibarray['Last Name'] = Util::getServerVar('HTTP_SN');
1194
        $shibarray['Display Name'] = Util::getServerVar('HTTP_DISPLAYNAME');
1195
        $shibarray['Email Address'] = Util::getServerVar('HTTP_MAIL');
1196
        $shibarray['Level of Assurance'] = Util::getServerVar('HTTP_ASSURANCE');
1197
        $shibarray['Affiliation'] = Util::getServerVar('HTTP_AFFILIATION');
1198
        $shibarray['OU'] = Util::getServerVar('HTTP_OU');
1199
        $shibarray['Member'] = Util::getServerVar('HTTP_MEMBER');
1200
        $shibarray['Authn Context'] = Util::getServerVar('HTTP_SHIB_AUTHNCONTEXT_CLASS');
1201
        $shibarray['Entitlement'] = Util::getServerVar('HTTP_ENTITLEMENT');
1202
        $shibarray['iTrustUIN'] = Util::getServerVar('HTTP_ITRUSTUIN');
1203
        $shibarray['Subject ID'] = Util::getServerVar('HTTP_SUBJECT_ID');
1204
        $shibarray['Pairwise ID'] = Util::getServerVar('HTTP_PAIRWISE_ID');
1205
1206
        // Make sure to use only the first of multiple values.
1207
        $attrs = array('ePPN','ePTID','First Name','Last Name',
1208
                       'Display Name','Email Address');
1209
        foreach ($attrs as $attr) {
1210
            if (($pos = strpos($shibarray[$attr], ';')) !== false) {
1211
                $shibarray[$attr] = substr($shibarray[$attr], 0, $pos);
1212
            }
1213
        }
1214
1215
        // Next, read the attributes for the given IdP. This includes
1216
        // values such as the display name for the IdP, the home page
1217
        // of the organization, and contact information.
1218
        $attrarray = array(
1219
            'Organization_Name',
1220
            'Home_Page',
1221
            'Support_Name',
1222
            'Support_Address',
1223
            'Technical_Name',
1224
            'Technical_Address',
1225
            'Administrative_Name',
1226
            'Administrative_Address'
1227
        );
1228
1229
        foreach ($attrarray as $attr) {
1230
            if (
1231
                ($this->exists($entityID)) &&
1232
                (isset($this->idparray[$entityID][$attr]))
1233
            ) {
1234
                $shibarray[preg_replace('/_/', ' ', $attr)] =
1235
                    $this->idparray[$entityID][$attr];
1236
            }
1237
        }
1238
1239
        // Special processing for OAuth 2.0 IdPs
1240
        if ($entityID == Util::getAuthzUrl('Google')) {
1241
            $shibarray['Organization Name'] = 'Google';
1242
            $shibarray['Home Page'] = 'https://myaccount.google.com';
1243
            $shibarray['Support Name'] = 'Google Help';
1244
            $shibarray['Support Address'] = '[email protected]';
1245
        } elseif ($entityID == Util::getAuthzUrl('GitHub')) {
1246
            $shibarray['Organization Name'] = 'GitHub';
1247
            $shibarray['Home Page'] = 'https://github.com';
1248
            $shibarray['Support Name'] = 'GitHub Help';
1249
            $shibarray['Support Address'] = '[email protected]';
1250
        } elseif ($entityID == Util::getAuthzUrl('ORCID')) {
1251
            $shibarray['Organization Name'] = 'ORCID';
1252
            $shibarray['Home Page'] = 'https://orcid.org';
1253
            $shibarray['Support Name'] = 'ORCID Help';
1254
            $shibarray['Support Address'] = '[email protected]';
1255
        }
1256
1257
        return $shibarray;
1258
    }
1259
1260
    /**
1261
     * DOM2Array
1262
     *
1263
     * This function sorts the passed-in DOM corresponding to
1264
     * idplist.xml and returns a 2D array where the keys are entityIDs
1265
     * and the values are arrays of attributes for each IdP.
1266
     *
1267
     * @param DOMDocument $dom The DOM containing the list of IdPs to convert
1268
     *        to an array. Returns null on error.
1269
     * @return array An array corresponding to the DOM of the IdPs.
1270
     */
1271
    public function DOM2Array($dom)
1272
    {
1273
        $retarr = null;
1274
1275
        if (!is_null($dom)) {
1276
            foreach ($dom->childNodes as $idps) {
1277
                // Top-level DOM has 'idps' only
1278
                foreach ($idps->childNodes as $idp) {
1279
                    // Loop through each <idp> element
1280
                    $entityID = $idp->attributes->item(0)->value;
1281
                    foreach ($idp->childNodes as $attr) {
1282
                        // Get all sub-attributes of the current <idp>
1283
                        if ($attr->nodeName != '#text') {
1284
                            $retarr[$entityID][$attr->nodeName] = $attr->nodeValue;
1285
                        }
1286
                    }
1287
                }
1288
            }
1289
        }
1290
1291
        return $retarr;
1292
    }
1293
1294
    /**
1295
     * array2DOM
1296
     *
1297
     * This function takes an array of IdPs (such as idparray) and
1298
     * returns a corresponding DOM which can be written to XML.
1299
     *
1300
     * @param array|null $arr An array corresponding to the idplist.
1301
     * @return DOMDocument A DOM for the idplist which can be written to XML.
1302
     */
1303
    public function array2DOM($arr)
1304
    {
1305
        $retdom = null;
1306
1307
        if (!is_null($arr)) {
1308
            $domi = new DOMImplementation();
1309
            $dom = $domi->createDocument(null, 'idps');
1310
            $idps = $dom->documentElement; // Top level <idps> element
1311
1312
            foreach ($arr as $entityID => $attrs) {
1313
                // Create an <idp> element to hold sub elements
1314
                $idp = $dom->createElement('idp');
1315
                $idp->setAttribute('entityID', $entityID);
1316
                $idps->appendChild($idp);
1317
                foreach ($attrs as $attr => $value) {
1318
                    $this->addNode($dom, $idp, $attr, $value);
1319
                }
1320
            }
1321
            $retdom = $this->sortDOM($dom);
1322
        }
1323
1324
        return $retdom;
1325
    }
1326
}
1327