Completed
Push — master ( a1987d...ef66da )
by Terrence
21:32 queued 06:26
created

IdpList   F

Complexity

Total Complexity 164

Size/Duplication

Total Lines 1237
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 452
dl 0
loc 1237
ccs 0
cts 593
cp 0
rs 2
c 0
b 0
f 0
wmc 164

36 Methods

Rating   Name   Duplication   Size   Complexity  
A getNonWhitelistedIdPs() 0 3 1
A getRandSIdPs() 0 3 1
B getInCommonIdPs() 0 19 9
A getWhitelistedIdPs() 0 3 1
A isBronze() 0 3 1
A isREFEDSRandS() 0 3 1
A isSilver() 0 3 1
A sortDOM() 0 24 1
A isInCommonRandS() 0 3 1
A entityIDExists() 0 3 1
A isAttributeSet() 0 4 2
A isSIRTFI() 0 3 1
A read() 0 6 3
A exists() 0 3 1
A getDisplayName() 0 7 2
F create() 0 396 83
A getOrganizationName() 0 7 2
A writeJSON() 0 18 4
A getLogout() 0 7 2
A writeXML() 0 24 5
A addNode() 0 11 1
A __construct() 0 12 3
A getFilename() 0 3 1
A getEntityIDs() 0 7 2
A readJSON() 0 14 4
A isWhitelisted() 0 3 1
A isRandS() 0 3 1
A setInCommonFilename() 0 3 1
A DOM2Array() 0 21 6
A array2DOM() 0 21 4
A setFilename() 0 3 1
A write() 0 6 3
A readXML() 0 20 4
A getInCommonFilename() 0 3 1
A isRegisteredByInCommon() 0 3 1
B getShibInfo() 0 63 7

How to fix   Complexity   

Complex Class

Complex classes like IdpList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use IdpList, and based on these observations, apply Extract Interface, too.

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 InCommon 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
 * There are several constants in the class that you
33
 * should set for your particular set up:
34
 *
35
 * DEFAULTIDPFILENAME - this is the full path and name
36
 *     of the processed IdP list file used by the CILogon
37
 *     Service. It should have read/write permissions for
38
 *     apache (via either owner or group).
39
 *
40
 * DEFAULTINCOMMONFILENAME - this is the full path and
41
 *     name of the InCommon metadata file used by the
42
 *     CILogon Service. It should have read permissions
43
 *     for apache (via either owner or group).
44
 *
45
 * TESTIDPFILENAME - this is the full path and name
46
 *     of an XML-formatted list of test IdPs. If found,
47
 *     these test IdPs will be added to the full IdP
48
 *     list when create()/write() is called. This file
49
 *     should have read/write permissions for apache.
50
 *
51
 * Note that this class previously defaulted to writing the idplist
52
 * as an XMl file and then reading that XML file back in. When all
53
 * InCommon and eduGAIN IdPs were 'whitelisted', the xpath queries
54
 * used on the read-in XML file were painfully slow. So now the default
55
 * is to read/write a JSON file and use a protected $idparray which is
56
 * an order of magnitude faster than xpath queries. You can still
57
 * read/write the XML file either by calling the associated read/write
58
 * methods (readXML/writeXML) or by setting $filetype='xml' in the
59
 * contructor. You will probably want to do this for the hourly cronjob
60
 * since the idplist.xml file is known to the world and should be
61
 * updated regularly.
62
 *
63
 * Example usage:
64
 *    require_once 'IdpList.php';
65
 *    // Read in extant idplist.json file, or create one from scratch
66
 *    $idplist = new IdpList();
67
 *    // Rescan InCommon metadata, update IdP list, and write to file
68
 *    $idplist->create();
69
 *    $idplist->setFilename('/tmp/newidplist.json');
70
 *    $idplist->write();
71
 */
72
class IdpList
73
{
74
75
    // Set the constants to correspond to your particular set up.
76
    /**
77
     * @var string DEFAULTIDPFILENAME The full path/filename of the
78
     *      generated list of IdPs in JSON format
79
     */
80
    const DEFAULTIDPFILENAME = '/var/www/html/include/idplist.json';
81
82
    /**
83
     * @var string DEFAULTINCOMMONFILENAME The full path/filename of the
84
     *      InCommon metadata XML file.
85
     */
86
    const DEFAULTINCOMMONFILENAME =
87
        '/var/cache/shibboleth/InCommon-metadata.xml';
88
89
    /**
90
     * @var string TESTIDPFILENAME The fill path/filename of the XML file
91
     *      containing test IdPs.
92
     */
93
    const TESTIDPFILENAME = '/var/www/html/include/testidplist.xml';
94
95
    /**
96
     * @var DOMDocument $idpdom A DOMDocument which holds the list of IdP
97
     *      entityIDs and their corresponding attributes.
98
     */
99
    protected $idpdom = null;
100
101
    /**
102
     * @var mixed $idparray An array version of $idpdom. It is used
103
     * primarily since searching an array is faster than xpath query.
104
     */
105
    protected $idparray = null;
106
107
    /**
108
     * @var string $idpfilename The name of the IdP list in JSON format.
109
     *      Defaults to DEFAULTIDPFILENAME.
110
     */
111
    protected $idpfilename;
112
113
    /**
114
     * @var string $incommonfilename The name of the InCommon metadata XML
115
     *      file. Defaults to DEFAULTINCOMMONFILENAME.
116
     */
117
    protected $incommonfilename;
118
119
    /**
120
     * __construct
121
     *
122
     * Default constructor. This method first attempts to read in an
123
     * existing idplist from a file and store it in the idparray /
124
     * idpdom. If a valid idplist file cannot be read and
125
     * $createfile is true, neW idparray / idpdom is created and
126
     * written to file.
127
     *
128
     * @param string $idpfilename (Optional) The name of the idplist file to
129
     *        read/write. Defaults to DEFAULTIDPFILENAME.
130
     * @param string $incommonfilename (Optional) The name of the InCommon
131
     *        metadata file to read. Defaults to DEFAULTINCOMMONFILENAME.
132
     * @param bool $createfile (Optional) Create idplist file if it doesn't
133
     *         exist? Defaults to true.
134
     * @param string $filetype (Optional) The type of file to read/write,
135
     *        one of 'xml' or 'json'. Defaults to 'json'.
136
     */
137
    public function __construct(
138
        $idpfilename = self::DEFAULTIDPFILENAME,
139
        $incommonfilename = self::DEFAULTINCOMMONFILENAME,
140
        $createfile = true,
141
        $filetype = 'json'
142
    ) {
143
        $this->setFilename($idpfilename);
144
        $this->setInCommonFilename($incommonfilename);
145
        $result = $this->read($filetype);
146
        if (($result === false) && ($createfile)) {
147
            $this->create();
148
            $this->write($filetype);
149
        }
150
    }
151
152
    /**
153
     * read
154
     *
155
     * This reads in the idplixt file based on the input filetype.
156
     * Defaults to reading in a JSON file.
157
     *
158
     * @param string $filetype (Optional) Type type of file to read, either
159
     *        'xml' or 'json'. Defaults to 'json'.
160
     * @return bool True if the idplist was read from file. False otherwise.
161
     */
162
    public function read($filetype = 'json')
163
    {
164
        if ($filetype == 'xml') {
165
            return $this->readXML();
166
        } elseif ($filetype == 'json') {
167
            return $this->readJSON();
168
        }
169
    }
170
171
    /**
172
     * readXML
173
     *
174
     * This method attempts to read in an existing idplist XML file and
175
     * store its contents in the class $idpdom DOMDocument. It also
176
     * converts the $idpdom to the internal $idparray if not already
177
     * set.
178
     *
179
     * @return bool True if the idplist file was read in correctly.
180
     *         False otherwise.
181
     */
182
    public function readXML()
183
    {
184
        $retval = false;  // Assume read failed
185
186
        $filename = $this->getFilename();
187
        if ((is_readable($filename)) &&
188
            (($dom = DOMDocument::load($filename, LIBXML_NOBLANKS)) !== false)) {
0 ignored issues
show
Bug Best Practice introduced by
The method DOMDocument::load() is not static, but was called statically. ( Ignorable by Annotation )

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

188
            (($dom = DOMDocument::/** @scrutinizer ignore-call */ load($filename, LIBXML_NOBLANKS)) !== false)) {
Loading history...
189
            $this->idpdom = $dom;
190
            $this->idpdom->preserveWhiteSpace = false;
191
            $this->idpdom->formatOutput = true;
192
            // Convert the read-in DOM to idparray for later use
193
            if (is_null($this->idparray)) {
194
                $this->idparray = $this->DOM2Array($this->idpdom);
195
            }
196
            $retval = true;
197
        } else {
198
            $this->idpdom = null;
199
        }
200
201
        return $retval;
202
    }
203
204
    /**
205
     * readJSON
206
     *
207
     * This method attempts to read in an existing idplist file
208
     * (containing JSON) and store its contents in the class $idparray.
209
     * Note that this does not update the internal $idpdom.
210
     *
211
     * @return bool True if the idplist file was read in correctly.
212
     *         False otherwise.
213
     */
214
    public function readJSON()
215
    {
216
        $retval = false;  // Assume read/json_decode failed
217
218
        $filename = $this->getFilename();
219
        if ((is_readable($filename)) &&
220
            (($contents = file_get_contents($filename)) !== false) &&
221
            (($tempjson = json_decode($contents, true)) !== null)) {
222
            $this->idparray = $tempjson;
223
            $retval = true;
224
        } else {
225
            $this->idparray = null;
226
        }
227
        return $retval;
228
    }
229
230
    /**
231
     * write
232
     *
233
     * This writes out the idplixt file based on the input filetype.
234
     * Defaults to writing a JSON file.
235
     *
236
     * @param string $filetype (Optional) Type type of file to write, either
237
     *        'xml' or 'json'. Defaults to 'json'.
238
     * @return bool True if the idplist was written to file. False
239
     *         otherwise.
240
     */
241
    public function write($filetype = 'json')
242
    {
243
        if ($filetype == 'xml') {
244
            return $this->writeXML();
245
        } elseif ($filetype == 'json') {
246
            return $this->writeJSON();
247
        }
248
    }
249
250
    /**
251
     * writeXML
252
     *
253
     * This method writes the class $idpdom to an XML file. It does
254
     * this by first writing to a temporary file in /tmp, then renaming
255
     * the temp file to the final idplist XML filename. Note that if
256
     * the internal $idpdom does not exist, it attempts to first
257
     * convert the internal $idparray to DOM and then write it.
258
     *
259
     * @return bool True if the idpdom was written to the idplist XML
260
     *         file. False otherwise.
261
     */
262
    public function writeXML()
263
    {
264
        $retval = false; // Assume write failed
265
266
        // If no idpdom, convert idparray to DOM
267
        if (is_null($this->idpdom)) {
268
            $this->idpdom = $this->array2DOM($this->idparray);
269
        }
270
271
        if (!is_null($this->idpdom)) {
272
            $this->idpdom->preserveWhiteSpace = false;
273
            $this->idpdom->formatOutput = true;
274
            $filename = $this->getFilename();
275
            $tmpfname = tempnam('/tmp', 'IDP');
276
            if (($this->idpdom->save($tmpfname) > 0) &&
277
                (@rename($tmpfname, $filename))) {
278
                chmod($filename, 0664);
279
                $retval = true;
280
            } else {
281
                @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

281
                /** @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...
282
            }
283
        }
284
285
        return $retval;
286
    }
287
288
    /**
289
     * writeJSON
290
     *
291
     * This method writes the class $idparray to a JSON file
292
     * It does this by first writing to a temporary file in /tmp,
293
     * then renaming the temp file to the final idplist JSON filename.
294
     *
295
     * @return bool True if the idparray was written to the idplist
296
     *         JSON file. False otherwise.
297
     */
298
    public function writeJSON()
299
    {
300
        $retval = false; // Assume write failed
301
302
        if (!is_null($this->idparray)) {
303
            $filename = $this->getFilename();
304
            $tmpfname = tempnam('/tmp', 'JSON');
305
            $json = json_encode($this->idparray, JSON_FORCE_OBJECT);
306
            if (((file_put_contents($tmpfname, $json)) !== false) &&
307
                (@rename($tmpfname, $filename))) {
308
                chmod($filename, 0664);
309
                $retval = true;
310
            } else {
311
                @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

311
                /** @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...
312
            }
313
        }
314
315
        return $retval;
316
    }
317
318
    /**
319
     * addNode
320
     *
321
     * This is a convenience method used by create() to add a new
322
     * child node (such as 'Organization_Name') to a parent idp node.
323
     * It also adds elements to the internal $idparray, thus creating
324
     * the internal idparray at the same time as the idpdom.
325
     *
326
     * @param \DOMDocument $dom A DOMDocument object
327
     * @param \DOMElement $idpnode A pointer to a parent <idp> DOMElement
328
     * @param string $nodename The name of the new child node DOMElement
329
     * @param string $nodevalue The value of the new child node DOMElement
330
     */
331
    private function addNode($dom, $idpnode, $nodename, $nodevalue)
332
    {
333
        $nodename = trim($nodename);    // Remove leading/trailing
334
        $nodevalue = trim($nodevalue);  // spaces, tabs, etc.
335
        $elemnode = $dom->createElement($nodename);
336
        $textnode = $dom->createTextNode($nodevalue);
337
        $elemnode->appendChild($textnode);
338
        $idpnode->appendChild($elemnode);
339
        // Also add element to the internal $idparray for later use
340
        $this->idparray[$idpnode->getAttribute('entityID')][$nodename] =
341
            $nodevalue;
342
    }
343
344
    /**
345
     * sortDOM
346
     *
347
     * This method is called by create() to sort the newly created
348
     * DOMDocument <idp> nodes by Display_Name. It uses an XSL
349
     * transformation to do the work. A new DOMDocument is created
350
     * and returned.
351
     *
352
     * @param DOMDocument $dom A DOMDocument to be sorted by Display_Name
353
     * @return DOMDocument A new DOMDocument with the <idp> elements sorted by
354
     *         Display_Name.
355
     */
356
    private function sortDOM($dom)
357
    {
358
        $xsltsort = <<<EOT
359
            <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
360
                            version="1.0">
361
            <xsl:output method="xml" encoding="UTF-8"/>
362
            <xsl:template match="node() | @*">
363
              <xsl:copy>
364
                <xsl:apply-templates select="node() | @*">
365
                  <xsl:sort select="translate(Display_Name,
366
                      'abcdefghijklmnopqrstuvwxyz',
367
                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"
368
                  data-type="text" order="ascending"/>
369
                </xsl:apply-templates>
370
              </xsl:copy>
371
            </xsl:template>
372
            </xsl:stylesheet>
373
EOT;
374
        $xsl = new DOMDocument('1.0');
375
        $xsl->loadXML($xsltsort);
376
        $proc = new XSLTProcessor();
377
        $proc->importStyleSheet($xsl);
378
        $newdom = $proc->transformToDoc($dom);
379
        return $newdom;
380
    }
381
382
    /**
383
     * create
384
     *
385
     * This method is used to populate the class $idpdom DOMDocument
386
     * using information from the InCommon metadata file. Note that
387
     * method updates $idpdom and $idparray. If you want to save either
388
     * to a file, be sure to call write() afterwards.
389
     *
390
     * @return bool True upon successful extraction of IdP information
391
     *         from the InCommon metadata file into the class
392
     *         $idpdom DOMDocument. False otherwise.
393
     */
394
    public function create()
395
    {
396
        $retval = false; // Assume create failed
397
        if (is_readable($this->getInCommonFilename())) {
398
            // Read in the InCommon metadata file
399
            $xmlstr = @file_get_contents($this->getInCommonFilename());
400
            if (strlen($xmlstr) > 0) {
401
                // Need to fix the namespace for Xpath queries to work
402
                $xmlstr = str_replace('xmlns=', 'ns=', $xmlstr);
403
                $xml = new SimpleXMLElement($xmlstr);
404
405
                // Select only IdPs from the InCommon metadata
406
                $result = $xml->xpath(
407
                    "//EntityDescriptor/IDPSSODescriptor" .
408
                    "/ancestor::EntityDescriptor"
409
                );
410
411
                // CIL-401 - Read in the global blacklist.txt file.
412
                // Don't add a <Whitelsited> tag for IdPs in this file.
413
                $blackidps = Util::readArrayFromFile(
414
                    '/var/www/html/include/blacklist.txt'
415
                );
416
417
                // Create a DOMDocument to build up the list of IdPs.
418
                $dom = DOMImplementation::createDocument(null, 'idps');
0 ignored issues
show
Bug Best Practice introduced by
The method DOMImplementation::createDocument() is not static, but was called statically. ( Ignorable by Annotation )

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

418
                /** @scrutinizer ignore-call */ 
419
                $dom = DOMImplementation::createDocument(null, 'idps');
Loading history...
419
                $idps = $dom->documentElement; // Top level <idps> element
420
421
                // Loop through the IdPs searching for desired attributes
422
                foreach ($result as $idx) {
423
                    // Need to set namespace prefixes for xpath queries to work
424
                    $sxe = $idx[0];
425
                    $sxe->registerXPathNamespace(
426
                        'mdattr',
427
                        'urn:oasis:names:tc:SAML:metadata:attribute'
428
                    );
429
                    $sxe->registerXPathNamespace(
430
                        'saml',
431
                        'urn:oasis:names:tc:SAML:2.0:assertion'
432
                    );
433
                    $sxe->registerXPathNamespace(
434
                        'mdrpi',
435
                        'urn:oasis:names:tc:SAML:metadata:rpi'
436
                    );
437
                    $sxe->registerXPathNamespace(
438
                        'mdui',
439
                        'urn:oasis:names:tc:SAML:metadata:ui'
440
                    );
441
442
                    // Skip any hide-from-discovery entries
443
                    $xp = $sxe->xpath(
444
                        "Extensions/mdattr:EntityAttributes/" .
445
                        "saml:Attribute[@Name='" .
446
                        "http://macedir.org/entity-category']/" .
447
                        "saml:AttributeValue"
448
                    );
449
                    if (($xp !== false) && (count($xp) > 0)) {
450
                        $hide = false;
451
                        foreach ($xp as $value) {
452
                            if ($value == 'http://refeds.org/category/hide-from-discovery') {
453
                                $hide = true;
454
                                break;
455
                            }
456
                        }
457
                        if ($hide) {
458
                            continue;
459
                        }
460
                    }
461
462
                    // Get the entityID of the IdP. Save it for later.
463
                    // The entityID will be the keys of the class idpdom.
464
                    $entityID = '';
465
                    $xp = $idx[0]->xpath('attribute::entityID');
466
                    if (($xp !== false) && (count($xp) > 0)) {
467
                        $entityID = (string)$xp[0]->entityID;
468
                    } else { // No entityID is bad!
469
                        continue;
470
                    }
471
472
                    // Create an <idp> element to hold sub elements
473
                    $idp = $dom->createElement('idp');
474
                    $idp->setAttribute('entityID', $entityID);
475
                    $idps->appendChild($idp);
476
477
                    // Search for the desired <idp> attribute sub-blocks
478
479
                    // Look for OrganizationDisplayName and mdui:DisplayName.
480
                    $Organization_Name = '';
481
                    $Display_Name = '';
482
483
                    $xp = $idx[0]->xpath(
484
                        "Organization/OrganizationDisplayName[starts-with(@xml:lang,'en')]"
485
                    );
486
                    if (($xp !== false) && (count($xp) > 0)) {
487
                        $Organization_Name = (string)$xp[0];
488
                    }
489
490
                    $xp = $sxe->xpath(
491
                        "IDPSSODescriptor/Extensions/mdui:UIInfo/mdui:DisplayName[starts-with(@xml:lang,'en')]"
492
                    );
493
                    if (($xp !== false) && (count($xp) > 0)) {
494
                        $Display_Name = (string)$xp[0];
495
                    }
496
497
                    // If neither OrganizationDisplayName nor mdui:DisplayName
498
                    // was found, then use the entityID as a last resort.
499
                    if ((strlen($Organization_Name) == 0) &&
500
                        (strlen($Display_Name) == 0)) {
501
                        $Organization_Name = $entityID;
502
                        $Display_Name = $entityID;
503
                    }
504
505
                    // Add nodes for both Organization_Name and Display_Name,
506
                    // using the value of the other if one is empty.
507
                    $this->addNode(
508
                        $dom,
509
                        $idp,
510
                        'Organization_Name',
511
                        ((strlen($Organization_Name) > 0) ?
512
                            $Organization_Name :
513
                            $Display_Name)
514
                    );
515
                    $this->addNode(
516
                        $dom,
517
                        $idp,
518
                        'Display_Name',
519
                        ((strlen($Display_Name) > 0) ?
520
                            $Display_Name :
521
                            $Organization_Name)
522
                    );
523
524
                    $xp = $idx[0]->xpath('Organization/OrganizationURL');
525
                    if (($xp !== false) && (count($xp) > 0)) {
526
                        $this->addNode($dom, $idp, 'Home_Page', (string)$xp[0]);
527
                    }
528
529
                    $name = '';
530
                    $xp = $idx[0]->xpath(
531
                        "ContactPerson[@contactType='support']/GivenName"
532
                    );
533
                    if (($xp !== false) && (count($xp) > 0)) {
534
                        $name = (string)$xp[0];
535
                    }
536
                    $xp = $idx[0]->xpath(
537
                        "ContactPerson[@contactType='support']/SurName"
538
                    );
539
                    if (($xp !== false) && (count($xp) > 0)) {
540
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
541
                            (string)($xp[0]);
542
                    }
543
                    if (strlen($name) > 0) {
544
                        $this->addNode($dom, $idp, 'Support_Name', $name);
545
                    }
546
547
                    $xp = $idx[0]->xpath(
548
                        "ContactPerson[@contactType='support']/EmailAddress"
549
                    );
550
                    if (($xp !== false) && (count($xp) > 0)) {
551
                        $this->addNode(
552
                            $dom,
553
                            $idp,
554
                            'Support_Address',
555
                            (string)$xp[0]
556
                        );
557
                    }
558
559
                    $name = '';
560
                    $xp = $idx[0]->xpath(
561
                        "ContactPerson[@contactType='technical']/GivenName"
562
                    );
563
                    if (($xp !== false) && (count($xp) > 0)) {
564
                        $name = (string)$xp[0];
565
                    }
566
                    $xp = $idx[0]->xpath(
567
                        "ContactPerson[@contactType='technical']/SurName"
568
                    );
569
                    if (($xp !== false) && (count($xp) > 0)) {
570
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
571
                            (string)($xp[0]);
572
                    }
573
                    if (strlen($name) > 0) {
574
                        $this->addNode($dom, $idp, 'Technical_Name', $name);
575
                    }
576
577
                    $xp = $idx[0]->xpath(
578
                        "ContactPerson[@contactType='technical']/EmailAddress"
579
                    );
580
                    if (($xp !== false) && (count($xp) > 0)) {
581
                        $this->addNode(
582
                            $dom,
583
                            $idp,
584
                            'Technical_Address',
585
                            (string)$xp[0]
586
                        );
587
                    }
588
589
                    $name = '';
590
                    $xp = $idx[0]->xpath(
591
                        "ContactPerson[@contactType='administrative']/GivenName"
592
                    );
593
                    if (($xp !== false) && (count($xp) > 0)) {
594
                        $name = (string)$xp[0];
595
                    }
596
                    $xp = $idx[0]->xpath(
597
                        "ContactPerson[@contactType='administrative']/SurName"
598
                    );
599
                    if (($xp !== false) && (count($xp) > 0)) {
600
                        $name .= ((strlen($name) > 0) ? ' ' : '') .
601
                            (string)($xp[0]);
602
                    }
603
                    if (strlen($name) > 0) {
604
                        $this->addNode($dom, $idp, 'Administrative_Name', $name);
605
                    }
606
607
                    $xp = $idx[0]->xpath(
608
                        "ContactPerson[@contactType='administrative']/EmailAddress"
609
                    );
610
                    if (($xp !== false) && (count($xp) > 0)) {
611
                        $this->addNode(
612
                            $dom,
613
                            $idp,
614
                            'Administrative_Address',
615
                            (string)$xp[0]
616
                        );
617
                    }
618
619
                    // Check for assurance-certification = silver, bronze, or SIRTFI
620
                    $xp = $sxe->xpath(
621
                        "Extensions/mdattr:EntityAttributes/" .
622
                        "saml:Attribute[@Name='" .
623
                        "urn:oasis:names:tc:SAML:attribute:" .
624
                        "assurance-certification']/saml:AttributeValue"
625
                    );
626
                    if (($xp !== false) && (count($xp) > 0)) {
627
                        foreach ($xp as $value) {
628
                            if ($value == 'http://id.incommon.org/assurance/silver') {
629
                                $this->addNode($dom, $idp, 'Silver', '1');
630
                            } elseif ($value == 'http://id.incommon.org/assurance/bronze') {
631
                                $this->addNode($dom, $idp, 'Bronze', '1');
632
                            } elseif ($value == 'https://refeds.org/sirtfi') {
633
                                $this->addNode($dom, $idp, 'SIRTFI', '1');
634
                            }
635
                        }
636
                    }
637
638
                    // Check for registered-by-incommon
639
                    $xp = $sxe->xpath(
640
                        "Extensions/mdattr:EntityAttributes/" .
641
                        "saml:Attribute[@Name='" .
642
                        "http://macedir.org/entity-category']/" .
643
                        "saml:AttributeValue"
644
                    );
645
                    if (($xp !== false) && (count($xp) > 0)) {
646
                        foreach ($xp as $value) {
647
                            if ($value == 'http://id.incommon.org/category/registered-by-incommon') {
648
                                $this->addNode(
649
                                    $dom,
650
                                    $idp,
651
                                    'Registered_By_InCommon',
652
                                    '1'
653
                                );
654
                                break;
655
                            }
656
                        }
657
                    }
658
659
                    // Check for research-and-scholarship
660
                    $xp = $sxe->xpath(
661
                        "Extensions/mdattr:EntityAttributes/" .
662
                        "saml:Attribute[@Name='" .
663
                        "http://macedir.org/entity-category-support']/" .
664
                        "saml:AttributeValue"
665
                    );
666
                    if (($xp !== false) && (count($xp) > 0)) {
667
                        $addedrands = false;
668
                        $incommonrands = false;
669
                        $refedsrands = false;
670
                        foreach ($xp as $value) {
671
                            if ($value == 'http://id.incommon.org/category/research-and-scholarship') {
672
                                $incommonrands = true;
673
                                $this->addNode($dom, $idp, 'InCommon_RandS', '1');
674
                            }
675
                            if ($value == 'http://refeds.org/category/research-and-scholarship') {
676
                                $refedsrands = true;
677
                                $this->addNode($dom, $idp, 'REFEDS_RandS', '1');
678
                            }
679
                            if ((!$addedrands) &&
680
                                ($incommonrands || $refedsrands)) {
681
                                $addedrands = true;
682
                                $this->addNode($dom, $idp, 'RandS', '1');
683
                            }
684
                        }
685
                    }
686
687
                    // CIL-558 Check for <SingleLogoutService>
688
                    $Logout = '';
689
                    // First, check for HTTP-Redirect version
690
                    $xp = $sxe->xpath("IDPSSODescriptor/SingleLogoutService" .
691
                        "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect']");
692
                    if (($xp !== false) && (count($xp) > 0)) {
693
                        $Logout = (string)($xp[0]->attributes())['Location'];
694
                    }
695
                    // If no HTTP-Redirect, check for HTTP-POST
696
                    if (empty($Logout)) {
697
                        $xp = $sxe->xpath("IDPSSODescriptor/SingleLogoutService" .
698
                            "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST']");
699
                        if (($xp !== false) && (count($xp) > 0)) {
700
                            $Logout = (string)($xp[0]->attributes())['Location'];
701
                        }
702
                    }
703
                    // Finally, a hack for Shibboleth-based IdPs.
704
                    // Check for <SingleSignOnService> HTTP-Redirect
705
                    // and regex for the built-in Simple Logout URL.
706
                    if (empty($Logout)) {
707
                        $xp = $sxe->xpath("IDPSSODescriptor/SingleSignOnService" .
708
                            "[@Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect']");
709
                        if (($xp !== false) && (count($xp) > 0)) {
710
                            $tmp = (string)($xp[0]->attributes())['Location'];
711
                            if (preg_match('|^(.*)/profile/|', $tmp, $matches)) {
712
                                $Logout = $matches[1] . '/profile/Logout';
713
                            }
714
                        }
715
                    }
716
                    if (!empty($Logout)) {
717
                        // If Shib IdP, transform URL into Simple Logout URL
718
                        // https://wiki.shibboleth.net/confluence/x/AAJSAQ
719
                        if (preg_match('|^(.*)/profile/|', $Logout, $matches)) {
720
                            $Logout = $matches[1] . '/profile/Logout';
721
                        }
722
                        $this->addNode($dom, $idp, 'Logout', $Logout);
723
                    }
724
725
                    // Add a <Whitelisted> block for all IdPs
726
                    // not in the blacklist.txt file.
727
                    if (!array_key_exists($entityID, $blackidps)) {
728
                        $this->addNode($dom, $idp, 'Whitelisted', '1');
729
                    }
730
                }
731
732
                // Read in any test IdPs and add them to the list
733
                if ((is_readable(static::TESTIDPFILENAME)) &&
734
                    (($dom2 = DOMDocument::load(
0 ignored issues
show
Bug Best Practice introduced by
The method DOMDocument::load() is not static, but was called statically. ( Ignorable by Annotation )

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

734
                    (($dom2 = DOMDocument::/** @scrutinizer ignore-call */ load(
Loading history...
735
                        static::TESTIDPFILENAME
736
                    )) !== false)) {
737
                    $idpnodes = $dom2->getElementsByTagName('idp');
738
                    foreach ($idpnodes as $idpnode) {
739
                        // Check if the entityID already exists. If so,
740
                        // delete it from both the idps DOM and the idparray
741
                        // and instead add the one from the testidplist.
742
                        $entityID = $idpnode->attributes->item(0)->value;
743
                        if (array_key_exists($entityID, $this->idparray)) {
744
                            // Easy - simply delete the array entry for the
745
                            // existing entityID
746
                            unset($this->idparray[$entityID]);
747
748
                            // Hard - search through the current DOM for a
749
                            // matching entityID to get the DOMNode, which
750
                            // can then be removed from the DOM.
751
                            $curridpnodes = $dom->getElementsByTagName('idp');
752
                            foreach ($curridpnodes as $curridpnode) {
753
                                $currEntityID =
754
                                    $curridpnode->attributes->item(0)->value;
755
                                if ($currEntityID == $entityID) {
756
                                    $idps->removeChild($curridpnode);
757
                                    break;
758
                                }
759
                            }
760
                        }
761
762
                        // Add the new idp node to the DOM
763
                        $node = $dom->importNode($idpnode, true);
764
                        $idps->appendChild($node);
765
766
                        // Add the testidplist nodes to the $idparray
767
                        foreach ($node->childNodes as $child) {
768
                            if ($child->nodeName != '#text') {
769
                                $this->idparray[$entityID][$child->nodeName] =
770
                                    $child->nodeValue;
771
                            }
772
                        }
773
                    }
774
                }
775
776
                // Sort the DOMDocument and idparray by Display_Name
777
                $this->idpdom = $this->sortDOM($dom);
778
                uasort($this->idparray, function ($a, $b) {
779
                    return strcasecmp(
780
                        $a['Display_Name'],
781
                        $b['Display_Name']
782
                    );
783
                });
784
785
                $retval = true;
786
            }
787
        }
788
789
        return $retval;
790
    }
791
792
    /**
793
     * getFilename
794
     *
795
     * This function returns a string of the full path of the IdP list
796
     * filename.  See also setFilename().
797
     *
798
     * @return string The IdP list filename.
799
     */
800
    public function getFilename()
801
    {
802
        return $this->idpfilename;
803
    }
804
805
    /**
806
     * setFilename
807
     *
808
     * This function sets the string of the full path of the IdP list
809
     * filename.  See also getFilename().
810
     *
811
     * @param string $filename he new name of the IdP list filename.
812
     */
813
    public function setFilename($filename)
814
    {
815
        $this->idpfilename = $filename;
816
    }
817
818
    /**
819
     * getInCommonFilename
820
     *
821
     * This function returns a string of the full path of the InCommon
822
     * metadata filename.  See also setInCommonFilename().
823
     *
824
     * @return string The InCommon metadata filename.
825
     */
826
    public function getInCommonFilename()
827
    {
828
        return $this->incommonfilename;
829
    }
830
831
    /**
832
     * setInCommonFilename
833
     *
834
     * This function sets the string of the full path of the InCommon
835
     * metadata filename.  See also getInCommonFilename().
836
     *
837
     * @param string $filename The new name of the InCommon metadata filename.
838
     */
839
    public function setInCommonFilename($filename)
840
    {
841
        $this->incommonfilename = $filename;
842
    }
843
844
    /**
845
     * getEntityIDs
846
     *
847
     * This method returns the entityIDs of the idplist as an array.
848
     *
849
     * @return array An array of the entityIDs
850
     */
851
    public function getEntityIDs()
852
    {
853
        $retarr = array();
854
        if (is_array($this->idparray)) {
855
            $retarr = array_keys($this->idparray);
856
        }
857
        return $retarr;
858
    }
859
860
    /**
861
     * getOrganizationName
862
     *
863
     * This function returns the Organization_Name of the selected
864
     * $entityID.
865
     *
866
     * @param string $entityID The entityID to search for
867
     * @return string The Organization_Name for the $entityID. Return
868
     *         string is empty if no matching $entityID found.
869
     */
870
    public function getOrganizationName($entityID)
871
    {
872
        $retval = '';
873
        if (isset($this->idparray[$entityID]['Organization_Name'])) {
874
            $retval = $this->idparray[$entityID]['Organization_Name'];
875
        }
876
        return $retval;
877
    }
878
879
    /**
880
     * getDisplayName
881
     *
882
     * This function returns the Display_Name of the selected
883
     * $entityID.
884
     *
885
     * @param string $entityID The entityID to search for
886
     * @return string The Display_Name for the $entityID. Return
887
     *         string is empty if no matching $entityID found.
888
     */
889
    public function getDisplayName($entityID)
890
    {
891
        $retval = '';
892
        if (isset($this->idparray[$entityID]['Display_Name'])) {
893
            $retval = $this->idparray[$entityID]['Display_Name'];
894
        }
895
        return $retval;
896
    }
897
898
    /**
899
     * getLogout
900
     *
901
     * This function returns the Logout URL of the selected $entityID.
902
     *
903
     * @param string $entityID The entityID to search for
904
     * @return string The Logout  URLfor the $entityID. Return
905
     *         string is empty if no matching $entityID found.
906
     */
907
    public function getLogout($entityID)
908
    {
909
        $retval = '';
910
        if (isset($this->idparray[$entityID]['Logout'])) {
911
            $retval = $this->idparray[$entityID]['Logout'];
912
        }
913
        return $retval;
914
    }
915
916
    /**
917
     * entityIDExists
918
     *
919
     * This function searchs for the given idp entityID.
920
     *
921
     * @param string $entityID The entityID to search for
922
     * @return bool True if the given entityID is found. False otherwise.
923
     */
924
    public function entityIDExists($entityID)
925
    {
926
        return (isset($this->idparray[$entityID]));
927
    }
928
929
    /**
930
     * exists
931
     *
932
     * This is simply a convenience function for entityIDExists.
933
     *
934
     * @param string $entityID The enityID to search for
935
     * @return bool True if the given entityID is found. False otherwise.
936
     */
937
    public function exists($entityID)
938
    {
939
        return $this->entityIDExists($entityID);
940
    }
941
942
    /**
943
     * isAttributeSet
944
     *
945
     * This function checks if the passed-in $attr is set to '1' for
946
     * the entityID, and returns true if so.
947
     *
948
     * @param string $entityID The enityID to search for.
949
     * @param string $attr The attribute in question.
950
     * @return bool True if the given attribute is '1' for the entityID.
951
     *         False otherwise.
952
     */
953
    public function isAttributeSet($entityID, $attr)
954
    {
955
        return (isset($this->idparray[$entityID][$attr]) &&
956
                     ($this->idparray[$entityID][$attr] == 1));
957
    }
958
959
    /**
960
     * isWhitelisted
961
     *
962
     * This method searches for the given entityID and checks if the
963
     *'Whitelisted' entry has been set to '1'.
964
     *
965
     * @param string $entityID The enityID to search for
966
     * @return bool True if the given entityID is marked 'Whitelisted'.
967
     *         False otherwise.
968
     */
969
    public function isWhitelisted($entityID)
970
    {
971
        return $this->isAttributeSet($entityID, 'Whitelisted');
972
    }
973
974
    /**
975
     * isSilver
976
     *
977
     * This method searches for the given entityID and checks if the
978
     *'Silver' entry has been set to '1'.
979
     *
980
     * @param string $entityID The enityID to search for
981
     * @return bool True if the given entityID is certified 'Silver'.
982
     *         False otherwise.
983
     */
984
    public function isSilver($entityID)
985
    {
986
        return $this->isAttributeSet($entityID, 'Silver');
987
    }
988
989
    /**
990
     * isBronze
991
     *
992
     * This method searches for the given entityID and checks if the
993
     *'Bronze' entry has been set to '1'.
994
     *
995
     * @param string $entityID The enityID to search for
996
     * @return bool True if the given entityID is certified 'Bronze'.
997
     *         False otherwise.
998
     */
999
    public function isBronze($entityID)
1000
    {
1001
        return $this->isAttributeSet($entityID, 'Bronze');
1002
    }
1003
1004
    /**
1005
     * isRandS
1006
     *
1007
     * This method searches for the given entityID and checks if the
1008
     *'RandS' entry has been set to '1'.
1009
     *
1010
     * @param string $entityID The enityID to search for
1011
     * @return bool True if the given entityID is listed as 'RandS'
1012
     *         (research-and-scholarship). False otherwise.
1013
     */
1014
    public function isRandS($entityID)
1015
    {
1016
        return $this->isAttributeSet($entityID, 'RandS');
1017
    }
1018
1019
    /**
1020
     * isInCommonRandS
1021
     *
1022
     * This method searches for the given entityID and checks if the
1023
     *'InCommon_RandS' entry has been set to '1'.
1024
     *
1025
     * @param string $entityID The enityID to search for
1026
     * @return bool True if the given entityID is listed as
1027
     *        'InCommon_RandS'. False otherwise.
1028
     */
1029
    public function isInCommonRandS($entityID)
1030
    {
1031
        return $this->isAttributeSet($entityID, 'InCommon_RandS');
1032
    }
1033
1034
    /**
1035
     * isREFEDSRandS
1036
     *
1037
     * This method searches for the given entityID and checks if the
1038
     *'REFEDS_RandS' entry has been set to '1'.
1039
     *
1040
     * @param string $entityID The enityID to search for
1041
     * @return bool True if the given entityID is listed as
1042
     *         'REFEDS_RandS'. False otherwise.
1043
     */
1044
    public function isREFEDSRandS($entityID)
1045
    {
1046
        return $this->isAttributeSet($entityID, 'REFEDS_RandS');
1047
    }
1048
1049
    /**
1050
     * isRegisteredByInCommon
1051
     *
1052
     * This method searches for the given entityID and checks if the
1053
     *'Registered_By_InCommon' entry has been set to '1'.
1054
     *
1055
     * @param string $entityID The enityID to search for
1056
     * @return bool True if the given entityID is listed as
1057
     *         'Registered_By_InCommon'. False otherwise.
1058
     */
1059
    public function isRegisteredByInCommon($entityID)
1060
    {
1061
        return $this->isAttributeSet($entityID, 'Registered_By_InCommon');
1062
    }
1063
1064
    /**
1065
     * isSIRTFI
1066
     *
1067
     * This method searches for the given entityID and checks if the
1068
     *'SIRTFI' entry has been set to '1'.
1069
     *
1070
     * @param string $entityID The enityID to search for
1071
     * @return bool True if the given entityID is listed as
1072
     *         SIRTFI. False otherwise.
1073
     */
1074
    public function isSIRTFI($entityID)
1075
    {
1076
        return $this->isAttributeSet($entityID, 'SIRTFI');
1077
    }
1078
1079
    /**
1080
     * getInCommonIdPs
1081
     *
1082
     * This method returns a two-dimensional array of InCommon IdPs.
1083
     * The primary key of the array is the entityID, the secondary key is
1084
     * either 'Organization_Name' (corresponds to OrganizationDisplayName)
1085
     * or 'Display_Name' (corresponds to mdui:DisplayName).
1086
     * If a non-null parameter is passed in it returns a subset of the
1087
     * InCommon IdPs. 0 means list only non-whitelisted IdPs, 1 means list
1088
     * only whitelisted IdPs, 2 means list only R&S IdPs.
1089
     *
1090
     * @param int $filter
1091
     *        null => all InCommonIdPs
1092
     *        0    => non-whitelisted InCommon IdPs
1093
     *        1    => whitelisted InCommon IdPs
1094
     *        2    => R&S InCommon IdPs
1095
     * $return array An array of InCommon IdP Organization Names and Display
1096
     *         Names, possibly filtered by whitelisted / non-whitelisted / R&S.
1097
     */
1098
    public function getInCommonIdPs($filter = null)
1099
    {
1100
        $retarr = array();
1101
1102
        foreach ($this->idparray as $key => $value) {
1103
            if ((!is_null($filter)) &&
1104
                (($filter === 0) &&
1105
                 ($this->isWhitelisted($key))) ||
1106
                (($filter === 1) &&
1107
                 (!$this->isWhitelisted($key))) ||
1108
                (($filter === 2) &&
1109
                 (!$this->isRandS($key)))) {
1110
                continue;
1111
            }
1112
            $retarr[$key]['Organization_Name'] = $this->idparray[$key]['Organization_Name'];
1113
            $retarr[$key]['Display_Name'] = $this->idparray[$key]['Display_Name'];
1114
        }
1115
1116
        return $retarr;
1117
    }
1118
1119
    /**
1120
     * getNonWhitelistedIdPs
1121
     *
1122
     * This method returns an array of non-whitelisted IdPs where the
1123
     * keys of the array are the entityIDs and the values are the
1124
     * pretty print Organization Names.
1125
     *
1126
     * @return array An array of non-whitelisted IdPs.
1127
     */
1128
    public function getNonWhitelistedIdPs()
1129
    {
1130
        return $this->getInCommonIdPs(0);
1131
    }
1132
1133
    /**
1134
     * getWhitelistedIdPs
1135
     *
1136
     * This method returns an array of whitelisted IdPs where the keys
1137
     * of the array are the entityIDs and the values are the
1138
     * pretty print Organization Names.
1139
     *
1140
     * @return array An array of whitelisted IdPs.
1141
     */
1142
    public function getWhitelistedIdPs()
1143
    {
1144
        return $this->getInCommonIdPs(1);
1145
    }
1146
1147
    /**
1148
     * getRandSIdPs
1149
     *
1150
     * This method returns an array of R&S IdPs where the keys
1151
     * of the array are the entityIDs and the values are the
1152
     * pretty print Organization Names.
1153
     *
1154
     * @return array An array of Research and Scholarship (R&S) IdPs.
1155
     */
1156
    public function getRandSIdPs()
1157
    {
1158
        return $this->getInCommonIdPs(2);
1159
    }
1160
1161
    /**
1162
     * getShibInfo
1163
     *
1164
     * This function returns an array with two types of Shibboleth
1165
     * information.  The first set of info is specific to the user's
1166
     * current Shibboleth session, such as REMOTE_USER. The second set
1167
     * of info reads info from the passed-in metadata file specific to
1168
     * the IdP, such as the pretty-print name of the IdP.
1169
     *
1170
     * @param string $entityID (Optional) The entityID to search for in
1171
     *        the InCommon metadata. Defaults to the HTTP header
1172
     *        HTTP_SHIB_IDENTITY_PROVIDER.
1173
     * @return array  An array containing the various shibboleth
1174
     *         attributes for the current Shibboleth session. The
1175
     *         keys of the array are 'pretty print' names of the
1176
     *         various attribute value names (such as
1177
     *         'User Identifier' for REMOTE_USER) and the values
1178
     *         of the array are the actual Shibboleth session values.
1179
     */
1180
    public function getShibInfo($entityID = '')
1181
    {
1182
        $shibarray = array();  // Array to be returned
1183
1184
        // Set the blob set of info, namely those shib attributes which
1185
        // were given by the IdP when the user authenticated.
1186
        if (strlen($entityID) == 0) {
1187
            $entityID = Util::getServerVar('HTTP_SHIB_IDENTITY_PROVIDER');
1188
        }
1189
        // CIL-254 - For LIGO backup IdPs, remap entityID to the main IdP
1190
        if (preg_match(
1191
            '%(https://login)[^\.]*(.ligo.org/idp/shibboleth)%',
1192
            $entityID,
1193
            $matches
1194
        )) {
1195
            $entityID = $matches[1] . $matches[2];
1196
        }
1197
        $shibarray['Identity Provider'] = $entityID;
1198
        $shibarray['User Identifier'] = Util::getServerVar('REMOTE_USER');
1199
        $shibarray['ePPN'] = Util::getServerVar('HTTP_EPPN');
1200
        $shibarray['ePTID'] = Util::getServerVar('HTTP_PERSISTENT_ID');
1201
        $shibarray['First Name'] = Util::getServerVar('HTTP_GIVENNAME');
1202
        $shibarray['Last Name'] = Util::getServerVar('HTTP_SN');
1203
        $shibarray['Display Name'] = Util::getServerVar('HTTP_DISPLAYNAME');
1204
        $shibarray['Email Address'] = Util::getServerVar('HTTP_MAIL');
1205
        $shibarray['Level of Assurance'] = Util::getServerVar('HTTP_ASSURANCE');
1206
        $shibarray['Affiliation'] = Util::getServerVar('HTTP_AFFILIATION');
1207
        $shibarray['OU'] = Util::getServerVar('HTTP_OU');
1208
        $shibarray['Member'] = Util::getServerVar('HTTP_MEMBER');
1209
        $shibarray['Authn Context'] = Util::getServerVar('HTTP_SHIB_AUTHNCONTEXT_CLASS');
1210
        $shibarray['Entitlement'] = Util::getServerVar('HTTP_ENTITLEMENT');
1211
1212
        // Make sure to use only the first of multiple values.
1213
        $attrs = array('ePPN','ePTID','First Name','Last Name',
1214
                       'Display Name','Email Address');
1215
        foreach ($attrs as $attr) {
1216
            if (($pos = strpos($shibarray[$attr], ';')) !== false) {
1217
                $shibarray[$attr] = substr($shibarray[$attr], 0, $pos);
1218
            }
1219
        }
1220
1221
        // Next, read the attributes for the given IdP. This includes
1222
        // values such as the display name for the IdP, the home page
1223
        // of the organization, and contact information.
1224
        $attrarray = array(
1225
            'Organization_Name',
1226
            'Home_Page',
1227
            'Support_Name',
1228
            'Support_Address',
1229
            'Technical_Name',
1230
            'Technical_Address',
1231
            'Administrative_Name',
1232
            'Administrative_Address'
1233
        );
1234
1235
        foreach ($attrarray as $attr) {
1236
            if (isset($this->idparray[$entityID][$attr])) {
1237
                $shibarray[preg_replace('/_/', ' ', $attr)] =
1238
                    $this->idparray[$entityID][$attr];
1239
            }
1240
        }
1241
1242
        return $shibarray;
1243
    }
1244
1245
    /**
1246
     * DOM2Array
1247
     *
1248
     * This function sorts the passed-in DOM corresponding to
1249
     * idplist.xml and returns a 2D array where the keys are entityIDs
1250
     * and the values are arrays of attributes for each IdP.
1251
     *
1252
     * @param DOMDocument The DOM containing the list of IdPs to convert to
0 ignored issues
show
Bug introduced by
The type CILogon\Service\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1253
     *        an array. Returns null on error.
1254
     * @return array An array corresponding to the DOM of the IdPs.
1255
     */
1256
    public function DOM2Array($dom)
1257
    {
1258
        $retarr = null;
1259
1260
        if (!is_null($dom)) {
1261
            foreach ($dom->childNodes as $idps) {
1262
                // Top-level DOM has 'idps' only
1263
                foreach ($idps->childNodes as $idp) {
1264
                    // Loop through each <idp> element
1265
                    $entityID = $idp->attributes->item(0)->value;
1266
                    foreach ($idp->childNodes as $attr) {
1267
                        // Get all sub-attributes of the current <idp>
1268
                        if ($attr->nodeName != '#text') {
1269
                            $retarr[$entityID][$attr->nodeName] = $attr->nodeValue;
1270
                        }
1271
                    }
1272
                }
1273
            }
1274
        }
1275
1276
        return $retarr;
1277
    }
1278
1279
    /**
1280
     * array2DOM
1281
     *
1282
     * This function takes an array of IdPs (such as idparray) and
1283
     * returns a corresponding DOM which can be written to XML.
1284
     *
1285
     * @param array $arr An array corresponding to the idplist.
1286
     * @return DOMDocument A DOM for the idplist which can be written to XML.
1287
     */
1288
    public function array2DOM($arr)
1289
    {
1290
        $retdom = null;
1291
1292
        if (!is_null($arr)) {
0 ignored issues
show
introduced by
The condition is_null($arr) is always false.
Loading history...
1293
            $dom = DOMImplementation::createDocument(null, 'idps');
0 ignored issues
show
Bug Best Practice introduced by
The method DOMImplementation::createDocument() is not static, but was called statically. ( Ignorable by Annotation )

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

1293
            /** @scrutinizer ignore-call */ 
1294
            $dom = DOMImplementation::createDocument(null, 'idps');
Loading history...
1294
            $idps = $dom->documentElement; // Top level <idps> element
1295
1296
            foreach ($arr as $entityID => $attrs) {
1297
                // Create an <idp> element to hold sub elements
1298
                $idp = $dom->createElement('idp');
1299
                $idp->setAttribute('entityID', $entityID);
1300
                $idps->appendChild($idp);
1301
                foreach ($attrs as $attr => $value) {
1302
                    $this->addNode($dom, $idp, $attr, $value);
1303
                }
1304
            }
1305
            $retdom = $this->sortDOM($dom);
1306
        }
1307
1308
        return $retdom;
1309
    }
1310
}
1311