Passed
Push — master ( b2c8b6...4d95f7 )
by Terrence
13:49
created

Content::redirectToGetShibUser()   B

Complexity

Conditions 10
Paths 20

Size

Total Lines 62
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 34
nc 20
nop 3
dl 0
loc 62
ccs 0
cts 42
cp 0
crap 110
rs 7.6666
c 2
b 0
f 0

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 CILogon\Service\MyProxy;
7
use CILogon\Service\PortalCookie;
8
use CILogon\Service\DBService;
9
use CILogon\Service\OAuth2Provider;
10
use CILogon\Service\Loggit;
11
use Net_LDAP2_Util;
12
13
/**
14
 * Content
15
 */
16
class Content
17
{
18
    /**
19
     * printHeader
20
     *
21
     * This function should be called to print out the main HTML header
22
     * block for each web page.  This gives a consistent look to the site.
23
     * Any style changes should go in the cilogon.css file.
24
     *
25
     * @param string $title The text in the window's titlebar
26
     * @param string $extra Optional extra text to go in the <head> block
27
     * @param bool $csrfcookie Set the CSRF cookie. Defaults to true.
28
     */
29
    public static function printHeader($title = '', $extra = '', $csrfcookie = true)
30
    {
31
        if ($csrfcookie) {
32
            $csrf = Util::getCsrf();
33
            $csrf->setTheCookie();
34
        }
35
36
        // Find the 'Powered By CILogon' image if specified by the skin
37
        $poweredbyimg = "/images/poweredbycilogon.png";
38
        $skin = Util::getSkin();
39
        $skinpoweredbyimg = (string)$skin->getConfigOption('poweredbyimg');
40
        if (
41
            (strlen($skinpoweredbyimg) > 0) &&
42
            (is_readable('/var/www/html' . $skinpoweredbyimg))
43
        ) {
44
            $poweredbyimg = $skinpoweredbyimg;
45
        }
46
47
        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
48
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
49
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
50
        <head><title>' , $title , '</title>
51
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
52
        <meta name="viewport" content="initial-scale=0.6" />
53
        <link rel="stylesheet" type="text/css" href="/include/cilogon.css" />
54
        ';
55
56
        $skin->printSkinLink();
57
58
        echo '
59
        <script type="text/javascript" src="/include/cilogon.js"></script>
60
        ' ;
61
62
        echo '
63
    <!--[if IE]>
64
        <style type="text/css">
65
          body { behavior: url(/include/csshover3.htc); }
66
        </style>
67
    <![endif]-->
68
        ';
69
70
        if (strlen($extra) > 0) {
71
            echo $extra;
72
        }
73
74
        echo '
75
        </head>
76
77
        <body>
78
79
        <div class="skincilogonlogo">
80
        <a target="_blank" href="http://www.cilogon.org/faq/"><img
81
        src="' , $poweredbyimg , '" alt="CILogon"
82
        title="CILogon Service" /></a>
83
        </div>
84
85
        <div class="logoheader">
86
           <h1><span>[CILogon Service]</span></h1>
87
        </div>
88
        <div class="pagecontent">
89
         ';
90
91
        if ((defined('BANNER_TEXT')) && (!empty(BANNER_TEXT))) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\BANNER_TEXT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
92
            echo '
93
            <div class="noticebanner">' , BANNER_TEXT , '</div>
94
            ';
95
        }
96
97
        $providerId = Util::getSessionVar('idp');
98
        if ($providerId == "urn:mace:incommon:idp.protectnetwork.org") {
99
            echo '
100
            <div class="noticebanner">Availability of the ProtectNetwork
101
            Identity Provider (IdP) will end after December 2014. Please
102
            consider using another IdP.</div>
103
            ';
104
        }
105
    }
106
107
    /**
108
     * printFooter
109
     *
110
     * This function should be called to print out the closing HTML block
111
     * for each web page.
112
     *
113
     * @param string $footer Optional extra text to be output before the
114
     * closing footer div.
115
     */
116
    public static function printFooter($footer = '')
117
    {
118
        if (strlen($footer) > 0) {
119
            echo $footer;
120
        }
121
122
        echo '
123
        <br class="clear" />
124
        <div class="footer">
125
        <a target="_blank" href="http://www.cilogon.org/faq"><img
126
        src="/images/questionIcon.png" class="floatrightclear"
127
        width="40" height="40" alt="CILogon FAQ" title="CILogon FAQ" /></a>
128
        <p>For questions about this site, please see the <a target="_blank"
129
        href="http://www.cilogon.org/faq">FAQs</a> or send email to <a
130
        href="mailto:[email protected]">help&nbsp;@&nbsp;cilogon.org</a>.</p>
131
        <p>Know <a target="_blank"
132
        href="http://ca.cilogon.org/responsibilities">your responsibilities</a>
133
        for using the CILogon Service.</p>
134
        <p>See <a target="_blank"
135
        href="http://ca.cilogon.org/acknowledgements">acknowledgements</a> of
136
        support for this site.</p>
137
        </div> <!-- Close "footer" div -->
138
        </div> <!-- Close "pagecontent" div -->
139
        </body>
140
        </html>
141
        ';
142
143
        session_write_close();
144
    }
145
146
    /**
147
     * printPageHeader
148
     *
149
     * This function prints a fancy formatted box with a single line of
150
     * text, suitable for a titlebox on each web page (to appear just below
151
     * the page banner at the very top). It prints a gradent border around
152
     * the four edges of the box and then outlines the inner box.
153
     *
154
     * @param string $text The text string to appear in the titlebox.
155
     */
156
    public static function printPageHeader($text)
157
    {
158
        echo '
159
        <div class="titlebox">' , $text , '
160
        </div>
161
        ';
162
    }
163
164
    /**
165
     * printFormHead
166
     *
167
     * This function prints out the opening <form> tag for displaying
168
     * submit buttons.  The first parameter is used for the 'action' value
169
     * of the <form>.  If omitted, getScriptDir() is called to get the
170
     * location of the current script.  This function outputs a hidden csrf
171
     * field in the form block.
172
     *
173
     * @param string $action (Optional) The value of the form's 'action'
174
     *        parameter. Defaults to getScriptDir().
175
     * @param string $method (Optional) The <form> 'method', one of 'get' or
176
     *        'post'. Defaults to 'post'.
177
     */
178
    public static function printFormHead(
179
        $action = '',
180
        $method = 'post'
181
    ) {
182
        static $formnum = 0;
183
184
        if (strlen($action) == 0) {
185
            $action = Util::getScriptDir();
186
        }
187
188
        echo '
189
        <form action="' , $action , '" method="' , $method , '"
190
         autocomplete="off" id="form' , sprintf("%02d", ++$formnum) , '">
191
        ';
192
        $csrf = Util::getCsrf();
193
        echo $csrf->hiddenFormElement();
194
    }
195
196
    /**
197
     * printWAYF
198
     *
199
     * This function prints the list of IdPs in a <select> form element
200
     * which can be printed on the main login page to allow the user to
201
     * select 'Where Are You From?'.  This function checks to see if a
202
     * cookie for the 'providerId' had been set previously, so that the
203
     * last used IdP is selected in the list.
204
     *
205
     * @param bool $showremember (Optional) Show the 'Remember this
206
     *        selection' checkbox? Defaults to true.
207
     * @param bool $samlidps (Optional) Show all SAML-based IdPs in
208
     *        selection list? Defaults to false, which means show
209
     *        only whitelisted IdPs.
210
     */
211
    public static function printWAYF($showremember = true, $samlidps = false)
212
    {
213
        $helptext = 'Check this box to bypass the welcome page on ' .
214
            'subsequent visits and proceed directly to the selected ' .
215
            'identity provider. You will need to clear your browser\'s ' .
216
            'cookies to return here.';
217
        $searchtext = "Enter characters to search for in the list above.";
218
219
        // Get an array of IdPs
220
        $idps = static::getCompositeIdPList($samlidps);
221
222
        $skin = Util::getSkin();
223
224
        // Check if the user had previously selected an IdP from the list.
225
        // First, check the portalcookie, then the 'normal' cookie.
226
        $keepidp = '';
227
        $providerId = '';
228
        $pc = new PortalCookie();
229
        $pn = $pc->getPortalName();
230
        if (strlen($pn) > 0) {
231
            $keepidp    = $pc->get('keepidp');
232
            $providerId = $pc->get('providerId');
233
        } else {
234
            $keepidp    = Util::getCookieVar('keepidp');
235
            $providerId = Util::getCookieVar('providerId');
236
        }
237
238
        // Make sure previously selected IdP is in list of available IdPs.
239
        if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
240
            $providerId = '';
241
        }
242
243
        // If no previous providerId, get from skin, or default to Google.
244
        if (strlen($providerId) == 0) {
245
            $initialidp = (string)$skin->getConfigOption('initialidp');
246
            if ((strlen($initialidp) > 0) && (isset($idps[$initialidp]))) {
247
                $providerId = $initialidp;
248
            } else {
249
                $providerId = Util::getAuthzUrl('Google');
250
            }
251
        }
252
253
        // Check if an OIDC client selected an IdP for the transaction.
254
        // If so, verify that the IdP is in the list of available IdPs.
255
        $useselectedidp = false;
256
        $idphintlist = static::getIdphintList($idps);
257
        if (!empty($idphintlist)) {
258
            $useselectedidp = true;
259
            $providerId = $idphintlist[0];
260
            $newidps = array();
261
            // Update the IdP selection list to show just the idphintlist.
262
            foreach ($idphintlist as $value) {
263
                $newidps[$value] = $idps[$value];
264
            }
265
            $idps = $newidps;
266
            // Re-sort the $idps by Display_Name for correct alphabetization.
267
            uasort($idps, function ($a, $b) {
268
                return strcasecmp(
269
                    $a['Display_Name'],
270
                    $b['Display_Name']
271
                );
272
            });
273
        }
274
275
        echo '
276
        <br />
277
        <div class="actionbox"';
278
279
        if (Util::getSessionVar('showhelp') == 'on') {
280
            echo ' style="width:92%;"';
281
        }
282
283
        echo '>
284
        <table class="helptable">
285
        <tr>
286
        <td class="actioncell">
287
288
          <form action="' , Util::getScriptDir() , '" method="post">
289
          <fieldset>
290
291
          <p>' , ($useselectedidp ? 'Selected' : 'Select An') ,
292
          ' Identity Provider:</p>
293
          ';
294
295
        // See if the skin has set a size for the IdP <select> list
296
        $selectsize = 4;
297
        $ils = $skin->getConfigOption('idplistsize');
298
        if ((!is_null($ils)) && ((int)$ils > 0)) {
299
            $selectsize = (int)$ils;
300
        }
301
302
        // When selected_idp is used, list size may be smaller
303
        if ($useselectedidp) {
304
            $selectsize = min($selectsize, count($idps));
305
        }
306
307
        echo '
308
          <p>
309
          <select name="providerId" id="providerId" size="' , $selectsize , '"
310
           onkeypress="enterKeySubmit(event)" ondblclick="doubleClickSubmit()"' ,
311
           // Hide the drop-down arrow in Firefox and Chrome
312
          ($useselectedidp ?
313
              'style="-moz-appearance:none;-webkit-appearance:none"' : '') ,
314
           '>
315
        ';
316
317
        foreach ($idps as $entityId => $names) {
318
            echo '    <option value="' , $entityId , '"';
319
            if ($entityId == $providerId) {
320
                echo ' selected="selected"';
321
            }
322
            echo '>' , Util::htmlent($names['Display_Name']) , '</option>' , "\n    ";
323
        }
324
325
        echo '  </select>
326
        </p>
327
328
        <p id="listsearch" class="zeroheight">
329
        <label for="searchlist" class="helpcursor" title="' ,
330
        $searchtext , '">Search:</label>
331
        <input type="text" name="searchlist" id="searchlist" value=""
332
        size="30" onkeyup="searchOptions(this.value)"
333
        title="' , $searchtext , '" />
334
    <!--[if IE]><input type="text" style="display:none;" disabled="disabled" size="1"/><![endif]-->
335
        </p>
336
        ';
337
338
        if ($showremember) {
339
            echo '
340
            <p>
341
            <label for="keepidp" title="' , $helptext ,
342
            '" class="helpcursor">Remember this selection:</label>
343
            <input type="checkbox" name="keepidp" id="keepidp" ' ,
344
            ((strlen($keepidp) > 0) ? 'checked="checked" ' : '') ,
345
            'title="' , $helptext , '" class="helpcursor" />
346
            </p>
347
            ';
348
        }
349
350
        echo '
351
        <p>
352
        ';
353
354
        echo Util::getCsrf()->hiddenFormElement();
355
356
        $lobtext = static::getLogOnButtonText();
357
358
        echo '
359
        <input type="submit" name="submit" class="submit helpcursor"
360
        title="Continue to the selected identity provider."
361
        value="' , $lobtext , '" id="wayflogonbutton" />
362
        <input type="hidden" name="previouspage" value="WAYF" />
363
        <input type="submit" name="submit" class="submit helpcursor"
364
        title="Cancel authentication and navigate away from this site."
365
        value="Cancel" id="wayfcancelbutton" />
366
        </p>
367
        ';
368
369
        $logonerror = Util::getSessionVar('logonerror');
370
        if (strlen($logonerror) > 0) {
371
            echo "<p class=\"logonerror\">$logonerror</p>";
372
            Util::unsetSessionVar('logonerror');
373
        }
374
375
        echo '
376
        <p class="privacypolicy">
377
        By selecting "' , $lobtext , '", you agree to <a target="_blank"
378
        href="http://ca.cilogon.org/policy/privacy">CILogon\'s privacy
379
        policy</a>.
380
        </p>
381
382
        </fieldset>
383
384
        </form>
385
      </td>
386
      ';
387
388
        if (Util::getSessionVar('showhelp') == 'on') {
389
            echo '
390
          <td class="helpcell">
391
          <div>
392
          ';
393
394
            if ($samlidps) { // SAML-based IdPs only means running from /testidp/
395
                echo '
396
                <p>
397
                CILogon facilitates secure access to CyberInfrastructure
398
                (<acronym title="CyberInfrastructure">CI</acronym>). In
399
                order to test your identity provider with the CILogon Service,
400
                you must first Log On. If your preferred identity provider is
401
                not listed, please contact <a
402
                href="mailto:[email protected]">[email protected]</a>, and
403
                we will try to add your identity provider in the future.
404
                </p>
405
                ';
406
            } else { // If not InCommon only, print help text for OpenID providers.
407
                echo '
408
                <p>
409
                CILogon facilitates secure access to CyberInfrastructure
410
                (<acronym title="CyberInfrastructure">CI</acronym>).
411
                In order to use the CILogon Service, you must first select
412
                an identity provider. An identity provider (IdP) is an
413
                organization where you have an account and can log on
414
                to gain access to online services.
415
                </p>
416
                <p>
417
                If you are a faculty, staff, or student member of a university
418
                or college, please select it for your identity provider.
419
                If your school is not listed, please contact <a
420
                href="mailto:[email protected]">[email protected]</a>, and we will
421
                try to add your school in the future.
422
                </p>
423
                ';
424
425
                $googleauthz = Util::getAuthzUrl('Google');
426
                if (isset($idps[$googleauthz])) {
427
                    echo '
428
                    <p>
429
                    If you have a <a target="_blank"
430
                    href="https://myaccount.google.com">Google</a>
431
                    account, you can select it for
432
                    authenticating to the CILogon Service.
433
                    </p>
434
                    ';
435
                }
436
                $githubauthz = Util::getAuthzUrl('GitHub');
437
                if (isset($idps[$githubauthz])) {
438
                    echo '
439
                    <p>
440
                    If you have a <a target="_blank"
441
                    href="https://github.com/settings/profile">GitHub</a>
442
                    account, you can select it for
443
                    authenticating to the CILogon Service.
444
                    </p>
445
                    ';
446
                }
447
                $orcidauthz = Util::getAuthzUrl('ORCID');
448
                if (isset($idps[$orcidauthz])) {
449
                    echo '
450
                    <p>
451
                    If you have a <a target="_blank"
452
                    href="https://orcid.org/my-orcid">ORCID</a>
453
                    account, you can select it for
454
                    authenticating to the CILogon Service.
455
                    </p>
456
                    ';
457
                }
458
            }
459
460
            echo '
461
          </div>
462
          </td>
463
          ';
464
        }
465
        echo '
466
      </tr>
467
      </table>
468
      </div>
469
      ';
470
    }
471
472
    /**
473
     * printCertInfo
474
     *
475
     * This function prints information related to the X.509 certificate
476
     * such as DN (distinguished name) and LOA (level of assurance).
477
     */
478
    public static function printCertInfo()
479
    {
480
        $dn = Util::getSessionVar('dn');
481
        // Strip off the email address from the pseudo-DN.
482
        $dn = Content::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
483
484
        echo '
485
        <table class="certinfo">
486
          <tr>
487
            <th>Certificate&nbsp;Subject:</th>
488
            <td>' , Util::htmlent($dn) , '</td>
489
          </tr>
490
          <tr>
491
            <th>Identity&nbsp;Provider:</th>
492
            <td>' , Util::getSessionVar('idpname') , '</td>
493
          </tr>
494
          <tr>
495
            <th><a target="_blank"
496
            href="http://ca.cilogon.org/loa">Level&nbsp;of&nbsp;Assurance:</a></th>
497
            <td>
498
        ';
499
500
        $loa = Util::getSessionVar('loa');
501
        if ($loa == 'openid') {
502
            echo '<a href="http://ca.cilogon.org/policy/openid"
503
                  target="_blank">OpenID</a>';
504
        } elseif ($loa == 'http://incommonfederation.org/assurance/silver') {
505
            echo '<a href="http://ca.cilogon.org/policy/silver"
506
                  target="_blank">Silver</a>';
507
        } else {
508
            echo '<a href="http://ca.cilogon.org/policy/basic"
509
                  target="_blank">Basic</a>';
510
        }
511
        echo '
512
            </td>
513
          </tr>
514
        </table>
515
        ';
516
    }
517
518
    /**
519
     * printUserAttributes
520
     *
521
     * This function shows the user the attributes released by their
522
     * selected IdP and saved in the PHP session.
523
     */
524
    public static function printUserAttributes()
525
    {
526
        $idplist = Util::getIdpList();
527
        $idp = Util::getSessionVar('idp');
528
        $gotattrs = Util::gotUserAttributes();
529
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
530
531
        echo '
532
        <div class="summary">
533
            <div id="userattrs1" style="display:' ,
534
            ($gotattrs ? "inline" : "none") ,
535
            '"><span class="expander"><a
536
            href="javascript:showHideDiv(\'userattrs\',-1)"><img
537
            src="/images/triright.gif" alt="&rArr;" width="14" height="14" />
538
            User Attributes</a></span>';
539
540
        // CIL-416 Show warning for missing ePPN
541
        if (
542
            ($samlidp) &&
543
            (empty(Util::getSessionVar('ePPN'))) &&
544
            (!empty(Util::getSessionVar('ePTID')))
545
        ) {
546
            Content::printIcon('warn', 'Some CILogon clients (e.g., Globus) require ePPN.');
547
        }
548
549
        echo '
550
            </div>
551
            <div id="userattrs2" style="display:' ,
552
                ($gotattrs ? "none" : "inline") ,
553
            '"><span class="expander"><a
554
            href="javascript:showHideDiv(\'userattrs\',-1)"><img
555
            src="/images/tridown.gif" alt="&dArr;" width="14" height="14" />
556
            User Attributes</a></span>
557
            </div>
558
            <br class="clear" />
559
            <div id="userattrs3" style="display:' ,
560
                ($gotattrs ? "none" : "inline") ,
561
            '">
562
563
            <table cellpadding="5">
564
              <tr class="odd">
565
                <th>Identity Provider (entityID):</th>
566
                <td>' , $idp , '</td>
567
                <td>';
568
569
        if (empty($idp)) {
570
            Content::printIcon('error', 'Missing the entityID of the IdP.');
571
        }
572
573
        echo '
574
                </td>
575
              </tr>
576
577
              <tr>
578
                <th>ePTID:</th>
579
                <td>' , Util::getSessionVar('ePTID') , '</td>
580
                <td>';
581
582
        if (
583
            ($samlidp) &&
584
            (empty(Util::getSessionVar('ePPN'))) &&
585
            (empty(Util::getSessionVar('ePTID')))
586
        ) {
587
            Content::printIcon('error', 'Must have either ePPN -OR- ePTID.');
588
        }
589
590
        echo '
591
                </td>
592
              </tr>
593
594
              <tr class="odd">
595
                <th>ePPN:</th>
596
                <td>' , Util::getSessionVar('ePPN') , '</td>
597
                <td>';
598
599
        if (($samlidp) && empty(Util::getSessionVar('ePPN'))) {
600
            if (empty(Util::getSessionVar('ePTID'))) {
601
                Content::printIcon('error', 'Must have either ePPN -OR- ePTID.');
602
            } else {
603
                Content::printIcon('warn', 'Some CILogon clients (e.g., Globus) require ePPN.');
604
            }
605
        }
606
607
        echo '
608
                </td>
609
              </tr>
610
611
              <tr>
612
                <th>OpenID:</th>
613
                <td>' , Util::getSessionVar('oidcID'), '</td>
614
                <td>';
615
616
        if (
617
            (!empty($idp)) &&
618
            (!$samlidp) &&
619
            empty(Util::getSessionVar('oidcID'))
620
        ) {
621
            Content::printIcon('error', 'Missing the OpenID identifier.');
622
        }
623
624
        echo '
625
                </td>
626
              </tr>
627
628
              <tr class="odd">
629
                <th>First Name (givenName):</th>
630
                <td>' ,Util::getSessionVar('firstname') , '</td>
631
                <td>';
632
633
        if (
634
            (empty(Util::getSessionVar('firstname'))) &&
635
            (empty(Util::getSessionVar('displayname')))
636
        ) {
637
            Content::printIcon('error', 'Must have either givenName + sn -OR- displayName.');
638
        }
639
640
        echo '
641
                </td>
642
              </tr>
643
644
              <tr>
645
                <th>Last Name (sn):</th>
646
                <td>' , Util::getSessionVar('lastname') , '</td>
647
                <td>';
648
649
        if (
650
            (empty(Util::getSessionVar('lastname'))) &&
651
            (empty(Util::getSessionVar('displayname')))
652
        ) {
653
            Content::printIcon('error', 'Must have either givenName + sn -OR- displayName.');
654
        }
655
656
        echo '
657
                </td>
658
              </tr>
659
660
              <tr class="odd">
661
                <th>Display Name (displayName):</th>
662
                <td>' , Util::getSessionVar('displayname') , '</td>
663
                <td>';
664
665
        if (
666
            (empty(Util::getSessionVar('displayname'))) &&
667
            ((empty(Util::getSessionVar('firstname'))) ||
668
            (empty(Util::getSessionVar('lastname'))))
669
        ) {
670
            Content::printIcon('error', 'Must have either displayName -OR- givenName + sn.');
671
        }
672
673
        echo '
674
                </td>
675
              </tr>
676
677
              <tr>
678
                <th>Email Address (email):</th>
679
                <td>' , Util::getSessionVar('emailaddr') , '</td>
680
                <td>';
681
682
        $emailvalid = filter_var(Util::getSessionVar('emailaddr'), FILTER_VALIDATE_EMAIL);
683
        if ((empty(Util::getSessionVar('emailaddr'))) || (!$emailvalid)) {
684
            Content::printIcon('error', 'Missing valid email address.');
685
        }
686
687
        echo '
688
                </td>
689
              </tr>
690
691
              <tr class="odd">
692
                <th>Level of Assurance (assurance):</th>
693
                <td>' , Util::getSessionVar('loa') , '</td>
694
                <td> </td>
695
              </tr>
696
697
              <tr>
698
                <th>AuthnContextClassRef:</th>
699
                <td>' , Util::getSessionVar('acr') , '</td>
700
                <td> </td>
701
              </tr>
702
703
              <tr class="odd">
704
                <th>Affiliation (affiliation):</th>
705
                <td>' , Util::getSessionVar('affiliation') , '</td>
706
                <td> </td>
707
              </tr>
708
709
              <tr>
710
                <th>Entitlement (entitlement):</th>
711
                <td>' , Util::getSessionVar('entitlement') , '</td>
712
                <td> </td>
713
              </tr>
714
715
              <tr class="odd">
716
                <th>Organizational Unit (ou):</th>
717
                <td>' , Util::getSessionVar('ou') , '</td>
718
                <td> </td>
719
              </tr>
720
721
              <tr>
722
                <th>Member (member):</th>
723
                <td>' , Util::getSessionVar('memberof') , '</td>
724
                <td> </td>
725
              </tr>
726
727
              <tr class="odd">
728
                <th>iTrustUIN (itrustuin):</th>
729
                <td>' , Util::getSessionVar('itrustuin') , '</td>
730
                <td> </td>
731
              </tr>
732
733
734
            </table>
735
            </div> <!-- userattrs3 -->
736
        </div> <!-- summary -->
737
        ';
738
    }
739
740
    /**
741
     * printIdPMetadata
742
     *
743
     * This function shows the metadata associated with the IdP saved to
744
     * the PHP session.
745
     */
746
    public static function printIdPMetadata()
747
    {
748
        $idplist = Util::getIdpList();
749
        $idp = Util::getSessionVar('idp');
750
        $gotattrs = Util::gotUserAttributes();
751
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
752
        $shibarray = $idplist->getShibInfo($idp);
753
754
        echo '
755
        <div class="summary">
756
            <div id="meta1" style="display:' ,
757
                ($gotattrs ? "inline" : "none") ,
758
            '"><span class="expander"><a
759
            href="javascript:showHideDiv(\'meta\',-1)"><img
760
            src="/images/triright.gif" alt="&rArr;" width="14" height="14" />
761
            Identity Provider Attributes</a></span>';
762
763
        // CIL-416 Check for eduGAIN IdPs without both REFEDS R&S and SIRTFI
764
        // since these IdPs are not allowed to get certificates.
765
        $eduGainWithoutRandSandSIRTFI = 0;
766
        if (
767
            ($samlidp) &&
768
            (!$idplist->isRegisteredByInCommon($idp)) &&
769
            ((!$idplist->isREFEDSRandS($idp)) ||
770
             (!$idplist->isSIRTFI($idp)))
771
        ) {
772
            $eduGainWithoutRandSandSIRTFI = 1;
773
        }
774
775
        if ($eduGainWithoutRandSandSIRTFI) {
776
            Content::printIcon('warn', 'This IdP does not support both ' .
777
                'REFEDS R&amp;S and SIRTFI. CILogon functionality may be limited.');
778
        }
779
780
        echo '
781
            </div>
782
            <div id="meta2" style="display:' ,
783
                ($gotattrs ? "none" : "inline") ,
784
            '"><span class="expander"><a
785
            href="javascript:showHideDiv(\'meta\',-1)"><img
786
            src="/images/tridown.gif" alt="&dArr;" width="14" height="14" />
787
            Identity Provider Attributes</a></span>
788
            </div>
789
            <br class="clear" />
790
            <div id="meta3" style="display:' ,
791
                ($gotattrs ? "none" : "inline") ,
792
            '">
793
794
            <table cellpadding="5">
795
              <tr class="odd">
796
                <th>Organization Name:</th>
797
                <td>' , @$shibarray['Organization Name'] , '</td>
798
                <td>';
799
800
        if (empty(@$shibarray['Organization Name'])) {
801
            Content::printIcon('error', 'Could not find ' .
802
                '&lt;OrganizationDisplayName&gt; in metadata.');
803
        }
804
805
        echo '
806
                </td>
807
              </tr>
808
              <tr>
809
                <th>Home Page:</th>
810
                <td><a target="_blank" href="' , @$shibarray['Home Page'] , '">' ,
811
                @$shibarray['Home Page'] , '</a></td>
812
                <td> </td>
813
              </tr>
814
815
              <tr class="odd">
816
                <th>Support Contact:</th>
817
        ';
818
        if (
819
            (!empty(@$shibarray['Support Name'])) ||
820
            (!empty(@$shibarray['Support Address']))
821
        ) {
822
            echo '
823
                <td>' , @$shibarray['Support Name'] , ' &lt;' ,
824
                        preg_replace('/^mailto:/', '', @$shibarray['Support Address']) , '&gt;</td>
825
                <td> </td>';
826
        }
827
        echo '
828
              </tr>
829
830
        ';
831
832
        if ($samlidp) {
833
            echo '
834
                  <tr>
835
                    <th>Technical Contact:</th>
836
            ';
837
            if (
838
                (!empty(@$shibarray['Technical Name'])) ||
839
                (!empty(@$shibarray['Technical Address']))
840
            ) {
841
                echo '
842
                    <td>' , @$shibarray['Technical Name'] , ' &lt;' ,
843
                            preg_replace('/^mailto:/', '', @$shibarray['Technical Address']) , '&gt;</td>
844
                    <td> </td>';
845
            }
846
            echo '
847
                  </tr>
848
849
                  <tr class="odd">
850
                    <th>Administrative Contact:</th>
851
            ';
852
            if (
853
                (!empty(@$shibarray['Administrative Name'])) ||
854
                (!empty(@$shibarray['Administrative Address']))
855
            ) {
856
                echo '
857
                    <td>' , @$shibarray['Administrative Name'] , ' &lt;' ,
858
                            preg_replace('/^mailto:/', '', @$shibarray['Administrative Address']) , '&gt;</td>
859
                    <td> </td>';
860
            }
861
            echo '
862
                  </tr>
863
864
                  <tr>
865
                    <th>Registered by InCommon:</th>
866
                    <td>' , ($idplist->isRegisteredByInCommon($idp) ? 'Yes' : 'No') , '</td>
867
                    <td> </td>
868
                  </tr>
869
870
                  <tr class="odd">
871
                    <th><a style="text-decoration:underline" target="_blank"
872
                    href="http://id.incommon.org/category/research-and-scholarship">InCommon R
873
                    &amp; S</a>:</th>
874
                    <td>' , ($idplist->isInCommonRandS($idp) ? 'Yes' : 'No') , '</td>
875
                    <td> </td>
876
                  </tr>
877
878
                  <tr>
879
                    <th><a style="text-decoration:underline" target="_blank"
880
                    href="http://refeds.org/category/research-and-scholarship">REFEDS
881
                    R &amp; S</a>:</th>
882
                    <td>' , ($idplist->isREFEDSRandS($idp) ? 'Yes' : 'No') , '</td>
883
                    <td>';
884
            if (
885
                ($eduGainWithoutRandSandSIRTFI &&
886
                !$idplist->isREFEDSRandS($idp))
887
            ) {
888
                Content::printIcon(
889
                    'warn',
890
                    'This IdP does not support both ' .
891
                    'REFEDS R&amp;S and SIRTFI. ' .
892
                    'CILogon functionality may be limited.'
893
                );
894
            }
895
896
            echo '              </td>
897
                  </tr>
898
899
                  <tr class="odd">
900
                    <th><a style="text-decoration:underline" target="_blank"
901
                           href="https://refeds.org/sirtfi">SIRTFI</a>:</th>
902
                    <td>' , ($idplist->isSIRTFI($idp) ? 'Yes' : 'No') , '</td>
903
                    <td>';
904
905
            if (
906
                ($eduGainWithoutRandSandSIRTFI &&
907
                !$idplist->isSIRTFI($idp))
908
            ) {
909
                Content::printIcon(
910
                    'warn',
911
                    'This IdP does not support both ' .
912
                    'REFEDS R&amp;S and SIRTFI. ' .
913
                    'CILogon functionality may be limited.'
914
                );
915
            }
916
917
            echo '              </td>
918
                  </tr>
919
920
                  <tr>
921
                    <th><a style="text-decoration:underline" target="_blank"
922
                    href="http://id.incommon.org/assurance/bronze">InCommon Bronze</a>:</th>
923
                    <td>' , ($idplist->isBronze($idp) ? 'Yes' : 'No') , '</td>
924
                    <td> </td>
925
                  </tr>
926
927
                  <tr class="odd">
928
                    <th><a style="text-decoration:underline" target="_blank"
929
                    href="http://id.incommon.org/assurance/silver">InCommon Silver</a>:</th>
930
                    <td>' , ($idplist->isSilver($idp) ? 'Yes' : 'No') , '</td>
931
                    <td> </td>
932
                  </tr>
933
934
                  <tr>
935
                    <th>Entity ID</th>
936
                    <td><a style="text-decoration:underline" target="_blank"
937
                    href="https://met.refeds.org/met/entity/',
938
                    rawurlencode($idp),
939
                    '">', $idp, '</td>
940
                    <td> </td>
941
                  </tr>
942
                  ';
943
        } // end if ($samlidp)
944
        echo '
945
              </table>
946
            </div>  <!-- meta3 -->
947
        </div>  <!-- summary -->
948
        ';
949
    }
950
951
    /**
952
     * handleLogOnButtonClicked
953
     *
954
     * This function is called when the user clicks the 'Log On' button
955
     * on the IdP selection page. It checks to see if the 'Remember this
956
     * selection' checkbox was checked and sets a cookie appropriately. It
957
     * also sets a cookie 'providerId' so the last chosen IdP will be
958
     * selected the next time the user visits the site. The function then
959
     * calls the appropriate 'redirectTo...' function to send the user
960
     * to the chosen IdP.
961
     */
962
    public static function handleLogOnButtonClicked()
963
    {
964
        // Get the list of currently available IdPs
965
        $idps = static::getCompositeIdPList();
966
967
        // Set the cookie for keepidp if the checkbox was checked
968
        $pc = new PortalCookie();
969
        $pn = $pc->getPortalName();
970
        if (strlen(Util::getPostVar('keepidp')) > 0) {
971
            if (strlen($pn) > 0) {
972
                $pc->set('keepidp', 'checked');
973
            } else {
974
                Util::setCookieVar('keepidp', 'checked');
975
            }
976
        } else {
977
            if (strlen($pn) > 0) {
978
                $pc->set('keepidp', '');
979
            } else {
980
                Util::unsetCookieVar('keepidp');
981
            }
982
        }
983
984
        // Get the user-chosen IdP from the posted form
985
        $providerId = Util::getPostVar('providerId');
986
987
        // Set the cookie for the last chosen IdP and redirect to it if in list
988
        if ((strlen($providerId) > 0) && (isset($idps[$providerId]))) {
989
            if (strlen($pn) > 0) {
990
                $pc->set('providerId', $providerId);
991
                $pc->write();
992
            } else {
993
                Util::setCookieVar('providerId', $providerId);
994
            }
995
            $providerName = Util::getAuthzIdP($providerId);
996
            if (in_array($providerName, Util::$oauth2idps)) {
997
                // Log in with an OAuth2 IdP
998
                static::redirectToGetOAuth2User($providerId);
999
            } else { // Use InCommon authn
1000
                static::redirectToGetShibUser($providerId);
1001
            }
1002
        } else { // IdP not in list, or no IdP selected
1003
            if (strlen($pn) > 0) {
1004
                $pc->set('providerId', '');
1005
                $pc->write();
1006
            } else {
1007
                Util::unsetCookieVar('providerId');
1008
            }
1009
            Util::setSessionVar('logonerror', 'Please select a valid IdP.');
1010
            printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1010
            /** @scrutinizer ignore-call */ 
1011
            printLogonPage();
Loading history...
1011
        }
1012
    }
1013
1014
    /**
1015
     * handleHelpButtonClicked
1016
     *
1017
     * This function is called when the user clicks on the 'Show Help' /
1018
     * 'Hide Help' button in the upper right corner of the page. It toggles
1019
     * the 'showhelp' session variable and redisplays the appropriate page
1020
     * with help now shown or hidden.
1021
     */
1022
    public static function handleHelpButtonClicked()
1023
    {
1024
        if (Util::getSessionVar('showhelp') == 'on') {
1025
            Util::unsetSessionVar('showhelp');
1026
        } else {
1027
            Util::setSessionVar('showhelp', 'on');
1028
        }
1029
1030
        $stage = Util::getSessionVar('stage');
1031
        if (static::verifyCurrentUserSession()) {
1032
            if ($stage == 'main') {
1033
                printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1033
                /** @scrutinizer ignore-call */ 
1034
                printMainPage();
Loading history...
1034
            } else {
1035
                printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1035
                /** @scrutinizer ignore-call */ 
1036
                printLogonPage();
Loading history...
1036
            }
1037
        } else {
1038
            printLogonPage();
1039
        }
1040
    }
1041
1042
    /**
1043
     * handleNoSubmitButtonClicked
1044
     *
1045
     * This function is the 'default' case when no 'submit' button has been
1046
     * clicked, or if the submit session variable is not set. It checks
1047
     * to see if either the <forceinitialidp> option is set, or if the
1048
     * 'Remember this selection' checkbox was previously checked. If so,
1049
     * then rediret to the appropriate IdP. Otherwise, print the main
1050
     * Log On page.
1051
     */
1052
    public static function handleNoSubmitButtonClicked()
1053
    {
1054
        $providerId = '';
1055
        $keepidp = '';
1056
        $selected_idp = '';
1057
        $redirect_uri = '';
1058
        $client_id = '';
1059
        $callbackuri = Util::getSessionVar('callbackuri');
1060
        $readidpcookies = true;  // Assume config options are not set
1061
        $skin = Util::getSkin();
1062
        $forceinitialidp = (int)$skin->getConfigOption('forceinitialidp');
1063
        $initialidp = (string)$skin->getConfigOption('initialidp');
1064
1065
        // If this is a OIDC transaction, get the redirect_uri and
1066
        // client_id parameters from the session var clientparams.
1067
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1068
        if (isset($clientparams['redirect_uri'])) {
1069
            $redirect_uri = $clientparams['redirect_uri'];
1070
        }
1071
        if (isset($clientparams['client_id'])) {
1072
            $client_id = $clientparams['client_id'];
1073
        }
1074
1075
        // Use the first element of the idphint list as the selected_idp.
1076
        $idphintlist = static::getIdphintList();
1077
        if (!empty($idphintlist)) {
1078
            $selected_idp = $idphintlist[0];
1079
        }
1080
1081
        if ((strlen($redirect_uri) > 0) || (strlen($client_id) > 0)) {
1082
            // CIL-431 - If the OAuth2/OIDC $redirect_uri or $client_id is set,
1083
            // then check for a match in the BYPASS_IDP_ARRAY to see if we
1084
            // should automatically redirect to a specific IdP. Used mainly
1085
            // by campus gateways.
1086
            $bypassidp = '';
1087
            foreach (BYPASS_IDP_ARRAY as $key => $value) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\BYPASS_IDP_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1088
                if (
1089
                    (preg_match($key, $redirect_uri)) ||
1090
                    (preg_match($key, $client_id))
1091
                ) {
1092
                    $bypassidp = $value;
1093
                    break;
1094
                }
1095
            }
1096
1097
            // CIL-613 - Next, check for a match in the ALLOW_BYPASS_ARRAY.
1098
            // If found, then allow the idphint/selected_idp to be used as the
1099
            // IdP to redirect to.
1100
            if (empty($bypassidp) && (!empty($selected_idp))) {
1101
                foreach (ALLOW_BYPASS_ARRAY as $value) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\ALLOW_BYPASS_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1102
                    if (
1103
                        (preg_match($value, $redirect_uri)) ||
1104
                        (preg_match($value, $client_id))
1105
                    ) {
1106
                        $bypassidp = $selected_idp;
1107
                        break;
1108
                    }
1109
                }
1110
            }
1111
1112
            if (!empty($bypassidp)) { // Match found!
1113
                $providerId = $bypassidp;
1114
                $keepidp = 'checked';
1115
                // To skip the next code blocks, unset a few variables.
1116
                $forceinitialidp = 0;     // Skip checking this option
1117
                $selected_idp = '';       // Skip any passed-in option
1118
                $readidpcookies = false;  // Don't read in the IdP cookies
1119
            }
1120
        }
1121
1122
        // If the <forceinitialidp> option is set, use either the
1123
        // <initialidp> or the selected_idp as the providerId, and use
1124
        // <forceinitialidp> as keepIdp. Otherwise, read the cookies
1125
        // 'providerId' and 'keepidp'.
1126
        if (
1127
            ($forceinitialidp == 1) &&
1128
            ((strlen($initialidp) > 0) || (strlen($selected_idp) > 0))
1129
        ) {
1130
            // If the <allowforceinitialidp> option is set, then make sure
1131
            // the callback / redirect uri is in the portal list.
1132
            $afii = $skin->getConfigOption('portallistaction', 'allowforceinitialidp');
1133
            if (
1134
                (is_null($afii)) || // Option not set, no need to check portal list
1135
                (((int)$afii == 1) &&
1136
                  (($skin->inPortalList($redirect_uri)) ||
1137
                   ($skin->inPortalList($client_id)) ||
1138
                   ($skin->inPortalList($callbackuri))))
1139
            ) {
1140
                // 'selected_idp' takes precedence over <initialidp>
1141
                if (strlen($selected_idp) > 0) {
1142
                    $providerId = $selected_idp;
1143
                } else {
1144
                    $providerId = $initialidp;
1145
                }
1146
                $keepidp = $forceinitialidp;
1147
                $readidpcookies = false; // Don't read in the IdP cookies
1148
            }
1149
        }
1150
1151
        // <initialidp> options not set, or portal not in portal list?
1152
        // Get idp and 'Remember this selection' from cookies instead.
1153
        $pc = new PortalCookie();
1154
        $pn = $pc->getPortalName();
1155
        if ($readidpcookies) {
1156
            // Check the portalcookie first, then the 'normal' cookies
1157
            if (strlen($pn) > 0) {
1158
                $keepidp    = $pc->get('keepidp');
1159
                $providerId = $pc->get('providerId');
1160
            } else {
1161
                $keepidp    = Util::getCookieVar('keepidp');
1162
                $providerId = Util::getCookieVar('providerId');
1163
            }
1164
        }
1165
1166
        // If both 'keepidp' and 'providerId' were set (and the
1167
        // providerId is a whitelisted IdP or valid OpenID provider),
1168
        // then skip the Logon page and proceed to the appropriate
1169
        // getuser script.
1170
        if ((strlen($providerId) > 0) && (strlen($keepidp) > 0)) {
1171
            // If selected_idp was specified at the OIDC authorize endpoint,
1172
            // make sure that it matches the saved providerId. If not,
1173
            // then show the Logon page and uncheck the keepidp checkbox.
1174
            if ((strlen($selected_idp) == 0) || ($selected_idp == $providerId)) {
1175
                $providerName = Util::getAuthzIdP($providerId);
1176
                if (in_array($providerName, Util::$oauth2idps)) {
1177
                    // Log in with an OAuth2 IdP
1178
                    static::redirectToGetOAuth2User($providerId);
1179
                } elseif (Util::getIdpList()->exists($providerId)) {
1180
                    // Log in with InCommon
1181
                    static::redirectToGetShibUser($providerId);
1182
                } else { // $providerId not in whitelist
1183
                    if (strlen($pn) > 0) {
1184
                        $pc->set('providerId', '');
1185
                        $pc->write();
1186
                    } else {
1187
                        Util::unsetCookieVar('providerId');
1188
                    }
1189
                    printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1189
                    /** @scrutinizer ignore-call */ 
1190
                    printLogonPage();
Loading history...
1190
                }
1191
            } else { // selected_idp does not match saved providerId
1192
                if (strlen($pn) > 0) {
1193
                    $pc->set('keepidp', '');
1194
                    $pc->write();
1195
                } else {
1196
                    Util::unsetCookieVar('keepidp');
1197
                }
1198
                printLogonPage();
1199
            }
1200
        } else { // One of providerId or keepidp was not set
1201
            printLogonPage();
1202
        }
1203
    }
1204
1205
    /**
1206
     * printIcon
1207
     *
1208
     * This function prints out the HTML for the little icons which can
1209
     * appear inline with other information.  This is accomplished via the
1210
     * use of wrapping the image in a <span> tag.
1211
     *
1212
     * @param string $icon The prefix of the '...Icon.png' image to be
1213
     *        shown. E.g., to show 'errorIcon.png', pass in 'error'.
1214
     * @param string $popuptext (Optionals) The popup 'title' text to be
1215
     *        displayed when the  mouse cursor hovers over the icon.
1216
     *        Defaults to empty string.
1217
     * @param string $class (Optionals) A CSS class for the icon. Will be
1218
     *        appended after the 'helpcursor' class. Defaults to empty
1219
     *        string.
1220
     */
1221
    public static function printIcon($icon, $popuptext = '', $class = '')
1222
    {
1223
        echo '<span';
1224
        if (strlen($popuptext) > 0) {
1225
            echo ' class="helpcursor ' , $class , '" title="' , $popuptext , '"';
1226
        }
1227
        echo '>&nbsp;<img src="/images/' , $icon , 'Icon.png"
1228
              alt="&laquo; ' , ucfirst($icon) , '"
1229
              width="14" height="14" /></span>';
1230
    }
1231
1232
    /**
1233
     * printHelpButton
1234
     *
1235
     * This function prints the 'Show Help' / 'Hide Help' button in the
1236
     * upper-right corner of the main box area on the page.
1237
     */
1238
    public static function printHelpButton()
1239
    {
1240
        echo '
1241
        <div class="helpbutton">
1242
        ';
1243
1244
        static::printFormHead();
1245
1246
        echo '
1247
          <input type="submit" name="submit" class="helpbutton" value="' ,
1248
          (Util::getSessionVar('showhelp') == 'on' ? 'Hide' : 'Show') , '&#10; Help " />
1249
          </form>
1250
        </div>
1251
        ';
1252
    }
1253
1254
    /**
1255
     * verifyCurrentUserSession
1256
     *
1257
     * This function verifies the contents of the PHP session.  It checks
1258
     * the following:
1259
     * (1) The persistent store 'uid', the Identity Provider 'idp', the
1260
     *     IdP Display Name 'idpname', and the 'status' (of getUser()) are
1261
     *     all non-empty strings.
1262
     * (2) The 'status' (of getUser()) is even (i.e. STATUS_OK).
1263
     * (3) If $providerId is passed-in, it must match 'idp'.
1264
     * If all checks are good, then this function returns true.
1265
     *
1266
     * @param string $providerId (Optional) The user-selected Identity
1267
     *        Provider. If set, make sure $providerId matches the PHP
1268
     *        session variable 'idp'.
1269
     * @return bool True if the contents of the PHP session ar valid.
1270
     *              False otherwise.
1271
     */
1272
    public static function verifyCurrentUserSession($providerId = '')
1273
    {
1274
        $retval = false;
1275
1276
        $idp       = Util::getSessionVar('idp');
1277
        $idpname   = Util::getSessionVar('idpname');
1278
        $uid       = Util::getSessionVar('uid');
1279
        $status    = Util::getSessionVar('status');
1280
        $dn        = Util::getSessionVar('dn');
1281
        $authntime = Util::getSessionVar('authntime');
1282
1283
        // CIL-410 When using the /testidp/ flow, the 'storeattributes'
1284
        // session var is set. In this case, the only attribute that
1285
        // is needed is 'idp' (entityID).
1286
        if (Util::getSessionVar('storeattributes') == '1') {
1287
            if (strlen($idp) > 0) {
1288
                $retval = true;
1289
            }
1290
        } elseif (
1291
            (strlen($uid) > 0) && (strlen($idp) > 0) &&
1292
            (strlen($idpname) > 0) && (strlen($status) > 0) &&
1293
            (strlen($dn) > 0) && (strlen($authntime) > 0) &&
1294
            (!($status & 1)) // All STATUS_OK codes are even
1295
        ) {
1296
            // Check for eduGAIN IdP and possible get cert context
1297
            if (Util::isEduGAINAndGetCert()) {
1298
                Util::unsetUserSessionVars();
1299
            } elseif ((strlen($providerId) == 0) || ($providerId == $idp)) {
1300
                // If $providerId passed in, make sure it matches the $idp
1301
                $retval = true;
1302
                Util::getSkin()->init(); // Does the IdP need a forced skin?
1303
            }
1304
        }
1305
1306
        return $retval;
1307
    }
1308
1309
    /**
1310
     * redirectToGetShibUser
1311
     *
1312
     * This method redirects control flow to the getuser script for
1313
     * If the first parameter (a whitelisted entityId) is not specified,
1314
     * we check to see if either the providerId PHP session variable or the
1315
     * providerId cookie is set (in that order) and use one if available.
1316
     * The function then checks to see if there is a valid PHP session
1317
     * and if the providerId matches the 'idp' in the session.  If so, then
1318
     * we don't need to redirect to '/secure/getuser/' and instead we
1319
     * we display the main page.  However, if the PHP session is not valid,
1320
     * then this function redirects to the '/secure/getuser/' script so as
1321
     * to do a Shibboleth authentication via mod_shib. When the providerId
1322
     * is non-empty, the SessionInitiator will automatically go to that IdP
1323
     * (i.e. without stopping at a WAYF).  This function also sets
1324
     * several PHP session variables that are needed by the getuser script,
1325
     * including the 'responsesubmit' variable which is set as the return
1326
     * 'submit' variable in the 'getuser' script.
1327
     *
1328
     * @param string $providerId (Optional) An entityId of the
1329
     *        authenticating IdP. If not specified (or set to the empty
1330
     *        string), we check providerId PHP session variable and
1331
     *        providerId cookie (in that order) for non-empty values.
1332
     * @param string $responsesubmit (Optional) The value of the PHP session
1333
     *       'submit' variable to be set upon return from the 'getuser'
1334
     *        script.  This is utilized to control the flow of this script
1335
     *        after 'getuser'. Defaults to 'gotuser'.
1336
     * @param string $responseurl (Optional) A response url for redirection
1337
     *        after successful processing at /secure/getuser/. Defaults to
1338
     *        the current script directory.
1339
     */
1340
    public static function redirectToGetShibUser(
1341
        $providerId = '',
1342
        $responsesubmit = 'gotuser',
1343
        $responseurl = null
1344
    ) {
1345
        // If providerId not set, try the cookie value
1346
        if (strlen($providerId) == 0) {
1347
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
1348
        }
1349
1350
        // If the user has a valid 'uid' in the PHP session, and the
1351
        // providerId matches the 'idp' in the PHP session, then
1352
        // simply go to the main page.
1353
        if (static::verifyCurrentUserSession($providerId)) {
1354
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1354
            /** @scrutinizer ignore-call */ 
1355
            printMainPage();
Loading history...
1355
        } else { // Otherwise, redirect to the getuser script
1356
            // Set PHP session varilables needed by the getuser script
1357
            Util::setSessionVar(
1358
                'responseurl',
1359
                (is_null($responseurl) ?
1360
                    Util::getScriptDir(true) : $responseurl)
1361
            );
1362
            Util::setSessionVar('submit', 'getuser');
1363
            Util::setSessionVar('responsesubmit', $responsesubmit);
1364
            Util::getCsrf()->setCookieAndSession();
1365
1366
            // Set up the 'header' string for redirection thru mod_shib
1367
            $mhn = static::getMachineHostname($providerId);
1368
            $redirect = "Location: https://$mhn/Shibboleth.sso/Login?target=" .
1369
                urlencode("https://$mhn/secure/getuser/");
1370
1371
            if (strlen($providerId) > 0) {
1372
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
1373
                if ($providerId == 'urn:mace:incommon:nih.gov') {
1374
                    $redirect = preg_replace(
1375
                        '%/Shibboleth.sso/Login%',
1376
                        '/Shibboleth.sso/NIHLogin',
1377
                        $redirect
1378
                    );
1379
                }
1380
1381
                $redirect .= '&providerId=' . urlencode($providerId);
1382
1383
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
1384
                $forceauthn = Util::getSessionVar('forceauthn');
1385
                Util::unsetSessionVar('forceauthn');
1386
                if ($forceauthn) {
1387
                    $redirect .= '&forceAuthn=true';
1388
                } elseif (strlen($forceauthn) == 0) {
1389
                    // 'forceauth' was not set to '0' in the session, so
1390
                    // check the skin's option instead.
1391
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
1392
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
1393
                        $redirect .= '&forceAuthn=true';
1394
                    }
1395
                }
1396
            }
1397
1398
            $log = new Loggit();
1399
            $log->info('Shibboleth Login="' . $redirect . '"');
1400
            header($redirect);
1401
            exit; // No further processing necessary
1402
        }
1403
    }
1404
1405
    /**
1406
     * redirectToGetOAuth2User
1407
     *
1408
     * This method redirects control flow to the getuser script for
1409
     * when the user logs in via OAuth 2.0. It first checks to see
1410
     * if we have a valid session. If so, we don't need to redirect and
1411
     * instead simply show the Get Certificate page. Otherwise, we start
1412
     * an OAuth 2.0 logon by composing a parameterized GET URL using
1413
     * the OAuth 2.0 endpoint.
1414
     *
1415
     * @param string $providerId (Optional) An entityId of the
1416
     *        authenticating IdP. If not specified (or set to the empty
1417
     *        string), we check providerId PHP session variable and
1418
     *        providerId cookie (in that order) for non-empty values.
1419
     * @param string $responsesubmit (Optional) The value of the PHP session
1420
     *        'submit' variable to be set upon return from the 'getuser'
1421
     *         script.  This is utilized to control the flow of this script
1422
     *         after 'getuser'. Defaults to 'gotuser'.
1423
     */
1424
    public static function redirectToGetOAuth2User(
1425
        $providerId = '',
1426
        $responsesubmit = 'gotuser'
1427
    ) {
1428
        // If providerId not set, try the cookie value
1429
        if (strlen($providerId) == 0) {
1430
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
1431
        }
1432
1433
        // If the user has a valid 'uid' in the PHP session, and the
1434
        // providerId matches the 'idp' in the PHP session, then
1435
        // simply go to the 'Download Certificate' button page.
1436
        if (static::verifyCurrentUserSession($providerId)) {
1437
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1437
            /** @scrutinizer ignore-call */ 
1438
            printMainPage();
Loading history...
1438
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
1439
            // Set PHP session varilables needed by the getuser script
1440
            Util::unsetSessionVar('logonerror');
1441
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
1442
            Util::setSessionVar('submit', 'getuser');
1443
            Util::setSessionVar('responsesubmit', $responsesubmit);
1444
            $csrf = Util::getCsrf();
1445
            $csrf->setCookieAndSession();
1446
            $extraparams = array();
1447
            $extraparams['state'] = $csrf->getTokenValue();
1448
1449
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
1450
            $forceauthn = Util::getSessionVar('forceauthn');
1451
            Util::unsetSessionVar('forceauthn');
1452
            if ($forceauthn) {
1453
                $extraparams['approval_prompt'] = 'force';
1454
            } elseif (strlen($forceauthn) == 0) {
1455
                // 'forceauth' was not set to '0' in the session, so
1456
                // check the skin's option instead.
1457
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
1458
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
1459
                    $extraparams['approval_prompt'] = 'force';
1460
                }
1461
            }
1462
1463
            // Get the provider name based on the provider authz URL
1464
            $providerName = Util::getAuthzIdP($providerId);
1465
1466
            // Get the authz URL and redirect
1467
            $oauth2 = new OAuth2Provider($providerName);
1468
            if (is_null($oauth2->provider)) {
1469
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
1470
                printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1470
                /** @scrutinizer ignore-call */ 
1471
                printLogonPage();
Loading history...
1471
            } else {
1472
                $authUrl = $oauth2->provider->getAuthorizationUrl(
1473
                    array_merge(
1474
                        $oauth2->authzUrlOpts,
1475
                        $extraparams
1476
                    )
1477
                );
1478
                header('Location: ' . $authUrl);
1479
                exit; // No further processing necessary
1480
            }
1481
        }
1482
    }
1483
1484
    /**
1485
     * printErrorBox
1486
     *
1487
     * This function prints out a bordered box with an error icon and any
1488
     * passed-in error HTML text.  The error icon and text are output to
1489
     * a <table> so as to keep the icon to the left of the error text.
1490
     *
1491
     * @param string $errortext HTML error text to be output
1492
     */
1493
    public static function printErrorBox($errortext)
1494
    {
1495
        echo '
1496
        <div class="errorbox">
1497
        <table cellpadding="5">
1498
        <tr>
1499
        <td valign="top">
1500
        ';
1501
        static::printIcon('error');
1502
        echo '&nbsp;
1503
        </td>
1504
        <td> ' , $errortext , '
1505
        </td>
1506
        </tr>
1507
        </table>
1508
        </div>
1509
        ';
1510
    }
1511
1512
    /**
1513
     * handleGotUser
1514
     *
1515
     * This function is called upon return from one of the getuser scripts
1516
     * which should have set the 'uid' and 'status' PHP session variables.
1517
     * It verifies that the status return is one of STATUS_OK (even
1518
     * values).  If not, we print an error message to the user.
1519
     */
1520
    public static function handleGotUser()
1521
    {
1522
        $log = new Loggit();
1523
        $uid = Util::getSessionVar('uid');
1524
        $status = Util::getSessionVar('status');
1525
1526
        // We must get and unset session vars BEFORE any HTML output since
1527
        // a redirect may go to another site, meaning we need to update
1528
        // the session cookie before we leave the cilogon.org domain.
1529
        $ePPN         = Util::getSessionVar('ePPN');
1530
        $ePTID        = Util::getSessionVar('ePTID');
1531
        $firstname    = Util::getSessionVar('firstname');
1532
        $lastname     = Util::getSessionVar('lastname');
1533
        $displayname  = Util::getSessionVar('displayname');
1534
        $emailaddr    = Util::getSessionVar('emailaddr');
1535
        $idp          = Util::getSessionVar('idp');
1536
        $idpname      = Util::getSessionVar('idpname');
1537
        $affiliation  = Util::getSessionVar('affiliation');
1538
        $ou           = Util::getSessionVar('ou');
1539
        $memberof     = Util::getSessionVar('memberof');
1540
        $acr          = Util::getSessionVar('acr');
1541
        $entitlement  = Util::getSessionVar('entitlement');
1542
        $itrustuin    = Util::getSessionVar('itrustuin');
1543
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1544
        $failureuri   = Util::getSessionVar('failureuri');
1545
1546
        // CIL-410 The /testidp/ flow is indicated by the presence of the
1547
        // 'storeattributes' PHP session var. In this case, simply show
1548
        // the main testidp page with user and IdP attributes.
1549
        if (!empty(Util::getSessionVar('storeattributes'))) {
1550
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1550
            /** @scrutinizer ignore-call */ 
1551
            printMainPage();
Loading history...
1551
            return;
1552
        }
1553
1554
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
1555
        // If found, set 'Proceed' button redirect appropriately.
1556
        $redirect = '';
1557
        $redirectform = '';
1558
        // First, check for OIDC redirect_uri, with parameters in <form>
1559
        if (isset($clientparams['redirect_uri'])) {
1560
            $redirect = $clientparams['redirect_uri'];
1561
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
1562
                '<input type="hidden" name="error_description" value="Missing attributes" />';
1563
            if (isset($clientparams['state'])) {
1564
                $redirectform .= '<input type="hidden" name="state" value="' .
1565
                    $clientparams['state'] . '" />';
1566
            }
1567
        }
1568
1569
        // Next, check for OAuth 1.0a
1570
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
1571
            $redirect = $failureuri . "?reason=missing_attributes";
1572
        }
1573
1574
        // If empty 'uid' or 'status' or odd-numbered status code, error!
1575
        if ((strlen($uid) == 0) || (strlen($status) == 0) || ($status & 1)) {
1576
            // Got all session vars by now, so okay to unset.
1577
            Util::unsetAllUserSessionVars();
1578
1579
            $log->error('Failed to getuser.');
1580
1581
            static::printHeader('Error Logging On');
1582
1583
            echo '
1584
            <div class="boxed">
1585
            ';
1586
1587
            if ($status == DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) {
1588
                // Check if the problem IdP was an OAuth2 IdP;
1589
                // probably no first/last name
1590
                if ($idpname == 'Google') {
1591
                    static::printErrorBox('
1592
                    <p>
1593
                    There was a problem logging on. It appears that you have
1594
                    attempted to use Google as your identity provider, but your
1595
                    name or email address was missing. To rectify this problem,
1596
                    go to the <a target="_blank"
1597
                    href="https://myaccount.google.com/privacy#personalinfo">Google
1598
                    Account Personal Information page</a>, and enter your first
1599
                    name, last name, and email address. (All other Google
1600
                    account information is not required by the CILogon Service.)
1601
                    </p>
1602
                    <p>
1603
                    After you have updated your Google account profile, click
1604
                    the "Proceed" button below and attempt to log on
1605
                    with your Google account again. If you have any questions,
1606
                    please contact us at the email address at the bottom of the
1607
                    page.</p>
1608
                    ');
1609
1610
                    echo '
1611
                    <div>
1612
                    ';
1613
                    static::printFormHead($redirect, 'get');
1614
                    echo '
1615
                    <p class="centered">
1616
                    <input type="hidden" name="providerId" value="' ,
1617
                    Util::getAuthzUrl('Google') , '" /> ' , $redirectform , '
1618
                    <input type="submit" name="submit" class="submit"
1619
                    value="Proceed" />
1620
                    </p>
1621
                    </form>
1622
                    </div>
1623
                    ';
1624
                } elseif ($idpname == 'GitHub') {
1625
                    static::printErrorBox('
1626
                    <p>
1627
                    There was a problem logging on. It appears that you have
1628
                    attempted to use GitHub as your identity provider, but your
1629
                    name or email address was missing. To rectify this problem,
1630
                    go to the <a target="_blank"
1631
                    href="https://github.com/settings/profile">GitHub
1632
                    Public Profile page</a>, and enter your name and email address.
1633
                    (All other GitHub account information is not required by
1634
                    the CILogon Service.)
1635
                    </p>
1636
                    <p>
1637
                    After you have updated your GitHub account profile, click
1638
                    the "Proceed" button below and attempt to log on
1639
                    with your GitHub account again. If you have any questions,
1640
                    please contact us at the email address at the bottom of the
1641
                    page.</p>
1642
                    ');
1643
1644
                    echo '
1645
                    <div>
1646
                    ';
1647
                    static::printFormHead($redirect, 'get');
1648
                    echo '
1649
                    <p class="centered">
1650
                    <input type="hidden" name="providerId" value="' ,
1651
                    Util::getAuthzUrl('GitHub') , '" /> ' , $redirectform , '
1652
                    <input type="submit" name="submit" class="submit"
1653
                    value="Proceed" />
1654
                    </p>
1655
                    </form>
1656
                    </div>
1657
                    ';
1658
                } elseif ($idpname == 'ORCID') {
1659
                    static::printErrorBox('
1660
                    <p>
1661
                    There was a problem logging on. It appears that you have
1662
                    attempted to use ORCID as your identity provider, but your
1663
                    name or email address was missing. To rectify this problem,
1664
                    go to your <a target="_blank"
1665
                    href="https://orcid.org/my-orcid">ORCID
1666
                    Profile page</a>, enter your name and email address, and
1667
                    make sure they can be viewed by Everyone.
1668
                    (All other ORCID account information is not required by
1669
                    the CILogon Service.)
1670
                    </p>
1671
                    <p>
1672
                    After you have updated your ORCID account profile, click
1673
                    the "Proceed" button below and attempt to log on
1674
                    with your ORCID account again. If you have any questions,
1675
                    please contact us at the email address at the bottom of the
1676
                    page.</p>
1677
                    ');
1678
1679
                    echo '
1680
                    <div>
1681
                    ';
1682
                    static::printFormHead($redirect, 'get');
1683
                    echo '
1684
                    <p class="centered">
1685
                    <input type="hidden" name="providerId" value="' ,
1686
                    Util::getAuthzUrl('ORCID') , '" /> ' , $redirectform , '
1687
                    <input type="submit" name="submit" class="submit"
1688
                    value="Proceed" />
1689
                    </p>
1690
                    </form>
1691
                    </div>
1692
                    ';
1693
                } else { // Problem was missing SAML attribute from Shib IdP
1694
                    static::printAttributeReleaseErrorMessage(
1695
                        $ePPN,
1696
                        $ePTID,
1697
                        $firstname,
1698
                        $lastname,
1699
                        $displayname,
1700
                        $emailaddr,
1701
                        $idp,
1702
                        $idpname,
1703
                        $affiliation,
1704
                        $ou,
1705
                        $memberof,
1706
                        $acr,
1707
                        $entitlement,
1708
                        $itrustuin,
1709
                        $clientparams,
1710
                        $redirect,
1711
                        $redirectform,
1712
                        Util::isEduGAINAndGetCert($idp, $idpname)
1713
                    );
1714
                }
1715
            } else {
1716
                static::printErrorBox('An internal error has occurred. System
1717
                    administrators have been notified. This may be a temporary
1718
                    error. Please try again later, or contact us at the the email
1719
                    address at the bottom of the page.');
1720
1721
                echo '
1722
                <div>
1723
                ';
1724
                static::printFormHead($redirect, 'get');
1725
                echo $redirectform , '
1726
                <input type="submit" name="submit" class="submit" value="Proceed" />
1727
                </form>
1728
                </div>
1729
                ';
1730
            }
1731
1732
            echo '
1733
            </div>
1734
            ';
1735
            static::printFooter();
1736
        } elseif (Util::isEduGAINAndGetCert($idp, $idpname)) {
1737
            // If eduGAIN IdP and session can get a cert, then error!
1738
            // Got all session vars by now, so okay to unset.
1739
            Util::unsetAllUserSessionVars();
1740
1741
            $log->error('Failed to getuser due to eduGAIN IdP restriction.');
1742
1743
            static::printHeader('Error Logging On');
1744
1745
            echo '
1746
            <div class="boxed">
1747
            ';
1748
            static::printAttributeReleaseErrorMessage(
1749
                $ePPN,
1750
                $ePTID,
1751
                $firstname,
1752
                $lastname,
1753
                $displayname,
1754
                $emailaddr,
1755
                $idp,
1756
                $idpname,
1757
                $affiliation,
1758
                $ou,
1759
                $memberof,
1760
                $acr,
1761
                $entitlement,
1762
                $itrustuin,
1763
                $clientparams,
1764
                $redirect,
1765
                $redirectform,
1766
                true
1767
            );
1768
1769
            echo '
1770
            </div>
1771
            ';
1772
            static::printFooter();
1773
        } else { // Got one of the STATUS_OK status codes
1774
            // Extra security check: Once the user has successfully authenticated
1775
            // with an IdP, verify that the chosen IdP was actually whitelisted.
1776
            // If not, then set error message and show Select an Identity Provider
1777
            // page again.
1778
            Util::getSkin()->init();  // Check for forced skin
1779
            $idps = static::getCompositeIdPList();
1780
            $providerId = Util::getSessionVar('idp');
1781
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
1782
                Util::setSessionVar(
1783
                    'logonerror',
1784
                    'Invalid IdP selected. Please try again.'
1785
                );
1786
                Util::sendErrorAlert(
1787
                    'Authentication attempt using non-whitelisted IdP',
1788
                    'A user successfully authenticated with an IdP, however, the
1789
selected IdP was not in the list of whitelisted IdPs as determined
1790
by the current skin. This might indicate the user attempted to
1791
circumvent the security check in "handleGotUser()" for valid
1792
IdPs for the skin.'
1793
                );
1794
                Util::unsetCookieVar('providerId');
1795
                Util::unsetAllUserSessionVars();
1796
                printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1796
                /** @scrutinizer ignore-call */ 
1797
                printLogonPage();
Loading history...
1797
            } else { // Got user successfully
1798
                static::gotUserSuccess();
1799
            }
1800
        }
1801
    }
1802
1803
    /**
1804
     * gotUserSuccess
1805
     *
1806
     * This function is called after the user has been successfully
1807
     * authenticated. If the 'status' session variable is STATUS_OK
1808
     * then it checks if we have a new or changed user and logs
1809
     * that appropriately. It then continues to the MainPage.
1810
     */
1811
    public static function gotUserSuccess()
1812
    {
1813
        $log = new Loggit();
1814
        $status = Util::getSessionVar('status');
1815
1816
        // If this is the first time the user has used the CILogon Service,
1817
        // and the flow is OAuth-based, send an alert if the name contains
1818
        // any HTML entities.
1819
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1820
        $callbackuri = Util::getSessionVar('callbackuri');
1821
1822
        if (
1823
            ($status == DBService::$STATUS['STATUS_NEW_USER']) &&
1824
            ((strlen($callbackuri) > 0) ||
1825
             (isset($clientparams['code'])))
1826
        ) {
1827
            // Extra check for new users: see if any HTML entities
1828
            // are in the user name. If so, send an email alert.
1829
            $dn = Util::getSessionVar('dn');
1830
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
1831
            $htmldn = Util::htmlent($dn);
1832
            if (strcmp($dn, $htmldn) != 0) {
1833
                Util::sendErrorAlert(
1834
                    'New user DN contains HTML entities',
1835
                    "htmlentites(DN) = $htmldn\n"
1836
                );
1837
            }
1838
        }
1839
1840
        // For a new user, or if the user got new attributes, just log it.
1841
        // Then proceed to the Main Page.
1842
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
1843
            $log->info('New User.');
1844
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
1845
            $log->info('User IdP attributes changed.');
1846
        }
1847
        printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1847
        /** @scrutinizer ignore-call */ 
1848
        printMainPage();
Loading history...
1848
    }
1849
1850
    /**
1851
     * generateP12
1852
     *
1853
     * This function is called when the user clicks the 'Get New
1854
     * Certificate' button. It first reads in the password fields and
1855
     * verifies that they are valid (i.e. they are long enough and match).
1856
     * Then it gets a credential from the MyProxy server and converts that
1857
     * certificate into a PKCS12 which is written to disk.  If everything
1858
     * succeeds, the temporary pkcs12 directory and lifetime is saved to
1859
     * the 'p12' PHP session variable, which is read later when the Main
1860
     * Page HTML is shown.
1861
     */
1862
    public static function generateP12()
1863
    {
1864
        $log = new Loggit();
1865
1866
        // Get the entered p12lifetime and p12multiplier and set the cookies
1867
        list($minlifetime, $maxlifetime) =
1868
            static::getMinMaxLifetimes('pkcs12', 9516);
1869
        $p12lifetime   = Util::getPostVar('p12lifetime');
1870
        $p12multiplier = Util::getPostVar('p12multiplier');
1871
        if (strlen($p12multiplier) == 0) {
1872
            $p12multiplier = 1;  // For ECP, p12lifetime is in hours
1873
        }
1874
        $lifetime = $p12lifetime * $p12multiplier;
1875
        if ($lifetime <= 0) { // In case user entered negative number
1876
            $lifetime = $maxlifetime;
1877
            $p12lifetime = $maxlifetime;
1878
            $p12multiplier = 1;  // maxlifetime is in hours
1879
        } elseif ($lifetime < $minlifetime) {
1880
            $lifetime = $minlifetime;
1881
            $p12lifetime = $minlifetime;
1882
            $p12multiplier = 1;  // minlifetime is in hours
1883
        } elseif ($lifetime > $maxlifetime) {
1884
            $lifetime = $maxlifetime;
1885
            $p12lifetime = $maxlifetime;
1886
            $p12multiplier = 1;  // maxlifetime is in hours
1887
        }
1888
        Util::setCookieVar('p12lifetime', $p12lifetime);
1889
        Util::setCookieVar('p12multiplier', $p12multiplier);
1890
        Util::setSessionVar('p12lifetime', $p12lifetime);
1891
        Util::setSessionVar('p12multiplier', $p12multiplier);
1892
1893
        // Verify that the password is at least 12 characters long
1894
        $password1 = Util::getPostVar('password1');
1895
        $password2 = Util::getPostVar('password2');
1896
        $p12password = Util::getPostVar('p12password');  // For ECP clients
1897
        if (strlen($p12password) > 0) {
1898
            $password1 = $p12password;
1899
            $password2 = $p12password;
1900
        }
1901
        if (strlen($password1) < 12) {
1902
            Util::setSessionVar(
1903
                'p12error',
1904
                'Password must have at least 12 characters.'
1905
            );
1906
            return; // SHORT PASSWORD - NO FURTHER PROCESSING NEEDED!
1907
        }
1908
1909
        // Verify that the two password entry fields matched
1910
        if ($password1 != $password2) {
1911
            Util::setSessionVar('p12error', 'Passwords did not match.');
1912
            return; // MISMATCHED PASSWORDS - NO FURTHER PROCESSING NEEDED!
1913
        }
1914
1915
        // Set the port based on the Level of Assurance
1916
        $port = 7512;
1917
        $loa = Util::getSessionVar('loa');
1918
        if ($loa == 'http://incommonfederation.org/assurance/silver') {
1919
            $port = 7514;
1920
        } elseif ($loa == 'openid') {
1921
            $port = 7516;
1922
        }
1923
1924
        $dn = Util::getSessionVar('dn');
1925
        if (strlen($dn) > 0) {
1926
            // Append extra info, such as 'skin', to be processed by MyProxy
1927
            $myproxyinfo = Util::getSessionVar('myproxyinfo');
1928
            if (strlen($myproxyinfo) > 0) {
1929
                $dn .= " $myproxyinfo";
1930
            }
1931
            // Attempt to fetch a credential from the MyProxy server
1932
            $cert = MyProxy::getMyProxyCredential(
1933
                $dn,
1934
                '',
1935
                MYPROXY_HOST,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYPROXY_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1936
                $port,
1937
                $lifetime,
1938
                '/var/www/config/hostcred.pem',
1939
                ''
1940
            );
1941
1942
            // The 'openssl pkcs12' command is picky in that the private
1943
            // key must appear BEFORE the public certificate. But MyProxy
1944
            // returns the private key AFTER. So swap them around.
1945
            $cert2 = '';
1946
            if (
1947
                preg_match(
1948
                    '/-----BEGIN CERTIFICATE-----([^-]+)' .
1949
                    '-----END CERTIFICATE-----[^-]*' .
1950
                    '-----BEGIN RSA PRIVATE KEY-----([^-]+)' .
1951
                    '-----END RSA PRIVATE KEY-----/',
1952
                    $cert,
1953
                    $match
1954
                )
1955
            ) {
1956
                $cert2 = "-----BEGIN RSA PRIVATE KEY-----" .
1957
                         $match[2] . "-----END RSA PRIVATE KEY-----\n" .
1958
                         "-----BEGIN CERTIFICATE-----" .
1959
                         $match[1] . "-----END CERTIFICATE-----";
1960
            }
1961
1962
            if (strlen($cert2) > 0) { // Successfully got a certificate!
1963
                // Create a temporary directory in /var/www/html/pkcs12/
1964
                $tdirparent = '/var/www/html/pkcs12/';
1965
                $polonum = '3';   // Prepend the polo? number to directory
1966
                if (preg_match('/(\d+)\./', php_uname('n'), $polomatch)) {
1967
                    $polonum = $polomatch[1];
1968
                }
1969
                $tdir = Util::tempDir($tdirparent, $polonum, 0770);
1970
                $p12dir = str_replace($tdirparent, '', $tdir);
1971
                $p12file = $tdir . '/usercred.p12';
1972
1973
                // Call the openssl pkcs12 program to convert certificate
1974
                exec('/bin/env ' .
1975
                     'RANDFILE=/tmp/.rnd ' .
1976
                     'CILOGON_PKCS12_PW=' . escapeshellarg($password1) . ' ' .
1977
                     '/usr/bin/openssl pkcs12 -export ' .
1978
                     '-passout env:CILOGON_PKCS12_PW ' .
1979
                     "-out $p12file " .
1980
                     '<<< ' . escapeshellarg($cert2));
1981
1982
                // Verify the usercred.p12 file was actually created
1983
                $size = @filesize($p12file);
1984
                if (($size !== false) && ($size > 0)) {
1985
                    $p12link = 'https://' . static::getMachineHostname() .
1986
                               '/pkcs12/' . $p12dir . '/usercred.p12';
1987
                    $p12 = (time() + 300) . " " . $p12link;
1988
                    Util::setSessionVar('p12', $p12);
1989
                    $log->info('Generated New User Certificate="' . $p12link . '"');
1990
                    //CIL-507 Special Log Message For XSEDE
1991
                    $log->info('USAGE email="' .
1992
                        Util::getSessionVar('emailaddr') . '" client="PKCS12"');
1993
                } else { // Empty or missing usercred.p12 file - shouldn't happen!
1994
                    Util::setSessionVar(
1995
                        'p12error',
1996
                        'Error creating certificate. Please try again.'
1997
                    );
1998
                    Util::deleteDir($tdir); // Remove the temporary directory
1999
                    $log->info('Error creating certificate - missing usercred.p12');
2000
                }
2001
            } else { // The myproxy-logon command failed - shouldn't happen!
2002
                Util::setSessionVar(
2003
                    'p12error',
2004
                    'Error! MyProxy unable to create certificate.'
2005
                );
2006
                $log->info('Error creating certificate - myproxy-logon failed');
2007
            }
2008
        } else { // Couldn't find the 'dn' PHP session value - shouldn't happen!
2009
            Util::setSessionVar(
2010
                'p12error',
2011
                'Missing username. Please enable cookies.'
2012
            );
2013
            $log->info('Error creating certificate - missing dn session variable');
2014
        }
2015
    }
2016
2017
    /**
2018
     * getLogOnButtonText
2019
     *
2020
     * This function checks the current skin to see if <logonbuttontext>
2021
     * has been configured.  If so, it returns that value.  Otherwise,
2022
     * it returns 'Log On'.
2023
     *
2024
     * @return string The text of the 'Log On' button for the WAYF, as
2025
     *         configured for the skin.  Defaults to 'Log On'.
2026
     */
2027
    public static function getLogOnButtonText()
2028
    {
2029
        $retval = 'Log On';
2030
        $lobt = Util::getSkin()->getConfigOption('logonbuttontext');
2031
        if (!is_null($lobt)) {
2032
            $retval = (string)$lobt;
2033
        }
2034
        return $retval;
2035
    }
2036
2037
    /**
2038
     * getSerialStringFromDN
2039
     *
2040
     * This function takes in a CILogon subject DN and returns just the
2041
     * serial string part (e.g., A325). This function is needed since the
2042
     * serial_string is not stored in the PHP session as a separate
2043
     * variable since it is always available in the 'dn' session variable.
2044
     *
2045
     * @param string $dn The certificate subject DN (typically found in the
2046
     *        session 'dn' variable)
2047
     * @return string The serial string extracted from the subject DN, or
2048
     *         empty string if DN is empty or wrong format.
2049
     */
2050
    public static function getSerialStringFromDN($dn)
2051
    {
2052
        $serial = ''; // Return empty string upon error
2053
2054
        // Strip off the email address, if present
2055
        $dn = preg_replace('/\s+email=.+$/', '', $dn);
2056
        // Find the 'CN=' entry
2057
        if (preg_match('%/DC=org/DC=cilogon/C=US/O=.*/CN=(.*)%', $dn, $match)) {
2058
            $cn = $match[1];
2059
            if (preg_match('/\s+([^\s]+)$/', $cn, $match)) {
2060
                $serial = $match[1];
2061
            }
2062
        }
2063
        return $serial;
2064
    }
2065
2066
    /**
2067
     * getEmailFromDN
2068
     *
2069
     * This function takes in a CILogon subject DN and returns just the
2070
     * email address part. This function is needed since the email address
2071
     * is not stored in the PHP session as a separate variable since it is
2072
     * always available in the 'dn' session variable.
2073
     *
2074
     * @param string $dn The certificate subject DN (typically found in the
2075
     *        session 'dn' variable)
2076
     * @return string The email address extracted from the subject DN, or
2077
     *         empty string if DN is empty or wrong format.
2078
     */
2079
    public static function getEmailFromDN($dn)
2080
    {
2081
        $email = ''; // Return empty string upon error
2082
        if (preg_match('/\s+email=(.+)$/', $dn, $match)) {
2083
            $email = $match[1];
2084
        }
2085
        return $email;
2086
    }
2087
2088
    /**
2089
     * reformatDN
2090
     *
2091
     * This function takes in a certificate subject DN with the email=...
2092
     * part already removed. It checks the skin to see if <dnformat> has
2093
     * been set. If so, it reformats the DN appropriately.
2094
     *
2095
     * @param string $dn The certificate subject DN (without the email=... part)
2096
     * @return string The certificate subject DN transformed according to
2097
     *         the value of the <dnformat> skin config option.
2098
     */
2099
    public static function reformatDN($dn)
2100
    {
2101
        $newdn = $dn;
2102
        $dnformat = (string)Util::getSkin()->getConfigOption('dnformat');
2103
        if (strlen($dnformat) > 0) {
2104
            if (
2105
                ($dnformat == 'rfc2253') &&
2106
                (preg_match(
2107
                    '%/DC=(.*)/DC=(.*)/C=(.*)/O=(.*)/CN=(.*)%',
2108
                    $dn,
2109
                    $match
2110
                ))
2111
            ) {
2112
                array_shift($match);
2113
                $m = array_reverse(Net_LDAP2_Util::escape_dn_value($match));
2114
                $newdn = "CN=$m[0],O=$m[1],C=$m[2],DC=$m[3],DC=$m[4]";
2115
            }
2116
        }
2117
        return $newdn;
2118
    }
2119
2120
    /**
2121
     * getMinMaxLifetimes
2122
     *
2123
     * This function checks the skin's configuration to see if either or
2124
     * both of minlifetime and maxlifetime in the specified config.xml
2125
     * block have been set. If not, default to minlifetime of 1 (hour) and
2126
     * the specified defaultmaxlifetime.
2127
     *
2128
     * @param string $section The XML section block from which to read the
2129
     *        minlifetime and maxlifetime values. Can be one of the
2130
     *        following: 'pkcs12' or 'delegate'.
2131
     * @param int $defaultmaxlifetime Default maxlifetime (in hours) for the
2132
     *        credential.
2133
     * @return array An array consisting of two entries: the minimum and
2134
     *         maximum lifetimes (in hours) for a credential.
2135
     */
2136
    public static function getMinMaxLifetimes($section, $defaultmaxlifetime)
2137
    {
2138
        $minlifetime = 1;    // Default minimum lifetime is 1 hour
2139
        $maxlifetime = $defaultmaxlifetime;
2140
        $skin = Util::getSkin();
2141
        $skinminlifetime = $skin->getConfigOption($section, 'minlifetime');
2142
        // Read the skin's minlifetime value from the specified section
2143
        if ((!is_null($skinminlifetime)) && ((int)$skinminlifetime > 0)) {
2144
            $minlifetime = max($minlifetime, (int)$skinminlifetime);
2145
            // Make sure $minlifetime is less than $maxlifetime;
2146
            $minlifetime = min($minlifetime, $maxlifetime);
2147
        }
2148
        // Read the skin's maxlifetime value from the specified section
2149
        $skinmaxlifetime = $skin->getConfigOption($section, 'maxlifetime');
2150
        if ((!is_null($skinmaxlifetime)) && ((int)$skinmaxlifetime) > 0) {
2151
            $maxlifetime = min($maxlifetime, (int)$skinmaxlifetime);
2152
            // Make sure $maxlifetime is greater than $minlifetime
2153
            $maxlifetime = max($minlifetime, $maxlifetime);
2154
        }
2155
2156
        return array($minlifetime, $maxlifetime);
2157
    }
2158
2159
    /**
2160
     * getMachineHostname
2161
     *
2162
     * This function is utilized in the formation of the URL for the
2163
     * PKCS12 credential download link and for the Shibboleth Single Sign-on
2164
     * session initiator URL. It returns a host-specific URL
2165
     * hostname by mapping the local machine hostname (as returned
2166
     * by 'uname -n') to an InCommon metadata cilogon.org hostname
2167
     * (e.g., polo2.cilogon.org). This function uses the HOSTNAME_ARRAY
2168
     * where the keys are the local machine hostname and
2169
     * the values are the external facing *.cilogon.org hostname.
2170
     * In case the local machine hostname cannot be found in the
2171
     * HOSTNAME_ARRAY, DEFAULT_HOSTNAME is returned.
2172
     *
2173
     * @param string $idp The entityID of the IdP used for potential
2174
     *        special handling (e.g., for Syngenta).
2175
     * @return string The full cilogon-specific hostname of this host.
2176
     */
2177
    public static function getMachineHostname($idp = '')
2178
    {
2179
        $retval = DEFAULT_HOSTNAME;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_HOSTNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2180
        // CIL-439 For Syngenta, use just a single 'hostname' value to
2181
        // match their Active Directory configuration for CILogon's
2182
        // assertionConsumerService URL. Otherwise, map the local
2183
        // hostname to a *.cilogon.org domain name.
2184
        if ($idp != 'https://sts.windows.net/06219a4a-a835-44d5-afaf-3926343bfb89/') {
2185
            $localhost = php_uname('n');
2186
            if (array_key_exists($localhost, HOSTNAME_ARRAY)) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\HOSTNAME_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2187
                $retval = HOSTNAME_ARRAY[$localhost];
2188
            }
2189
        }
2190
        return $retval;
2191
    }
2192
2193
    /**
2194
     * getCompositeIdPList
2195
     *
2196
     * This function generates a list of IdPs to display in the 'Select
2197
     * An Identity Provider' box on the main CILogon page or on the
2198
     * TestIdP page. For the main CILogon page, this is a filtered list of
2199
     * IdPs based on the skin's whitelist/blacklist and the global
2200
     * blacklist file. For the TestIdP page, the list is all InCommon IdPs.
2201
     *
2202
     * @param bool $samlidps (Optional) Show all SAML-based IdPs in
2203
     *        selection list? Defaults to false, which means show only
2204
     *        whitelisted IdPs.
2205
     * @return array A two-dimensional array where the primary key is the
2206
     *         entityID and the secondary key is either 'Display_Name'
2207
     *         or 'Organization_Name'.
2208
     */
2209
    public static function getCompositeIdPList($samlidps = false)
2210
    {
2211
        $retarray = array();
2212
2213
        $idplist = Util::getIdpList();
2214
        if ($samlidps) { // Get all SAML-based IdPs only
2215
            $retarray = $idplist->getSAMLIdPs();
2216
        } else { // Get the selected InCommon IdPs, plus maybe OAuth2 IdPs
2217
            $skin = Util::getSkin();
2218
2219
            // Check if the skin's config.xml has set the
2220
            // 'registeredbyincommonidps' option, which restricts the SAML-
2221
            // based IdPs to those with the <Registered_By_InCommon> tag.
2222
            // Otherwise, just get the SAML-based IdPs that have the
2223
            // <Whitelisted> tag. Note that the skin's <idpwhitelist>
2224
            // is still consulted in either case (below).
2225
            $registeredbyincommonidps = $skin->getConfigOption('registeredbyincommonidps');
2226
            if (
2227
                (!is_null($registeredbyincommonidps)) &&
2228
                ((int)$registeredbyincommonidps == 1)
2229
            ) {
2230
                $retarray = $idplist->getRegisteredByInCommonIdPs();
2231
            } else {
2232
                $retarray = $idplist->getWhitelistedIdPs();
2233
            }
2234
2235
            // Add all OAuth2 IdPs to the list
2236
            foreach (Util::$oauth2idps as $value) {
2237
                // CIL-617 Show OAuth2 IdPs only if client_id is configured
2238
                $client_id = constant(strtoupper($value) . '_OAUTH2_CLIENT_ID');
2239
                if (!empty($client_id)) {
2240
                    $retarray[Util::getAuthzUrl($value)]['Organization_Name'] =
2241
                        $value;
2242
                    $retarray[Util::getAuthzUrl($value)]['Display_Name'] =
2243
                        $value;
2244
                }
2245
            }
2246
2247
            // Check to see if the skin's config.xml has a whitelist of IDPs.
2248
            // If so, go thru master IdP list and keep only those IdPs in the
2249
            // config.xml's whitelist.
2250
            if ($skin->hasIdpWhitelist()) {
2251
                foreach ($retarray as $entityId => $names) {
2252
                    if (!$skin->idpWhitelisted($entityId)) {
2253
                        unset($retarray[$entityId]);
2254
                    }
2255
                }
2256
            }
2257
            // Next, check to see if the skin's config.xml has a blacklist of
2258
            // IdPs. If so, cull down the master IdP list removing 'bad' IdPs.
2259
            if ($skin->hasIdpBlacklist()) {
2260
                $idpblacklist = $skin->getConfigOption('idpblacklist');
2261
                foreach ($idpblacklist->idp as $blackidp) {
2262
                    unset($retarray[(string)$blackidp]);
2263
                }
2264
            }
2265
        }
2266
2267
        // Fix for CIL-174 - As suggested by Keith Hazelton, replace commas and
2268
        // hyphens with just commas.
2269
        $regex = '/(University of California)\s*[,-]\s*/';
2270
        foreach ($retarray as $entityId => $names) {
2271
            if (preg_match($regex, $names['Organization_Name'])) {
2272
                $retarray[$entityId]['Organization_Name'] =
2273
                    preg_replace($regex, '$1, ', $names['Organization_Name']);
2274
            }
2275
            if (preg_match($regex, $names['Display_Name'])) {
2276
                $retarray[$entityId]['Display_Name'] =
2277
                    preg_replace($regex, '$1, ', $names['Display_Name']);
2278
            }
2279
        }
2280
2281
        // Re-sort the retarray by Display_Name for correct alphabetization.
2282
        uasort($retarray, function ($a, $b) {
2283
            return strcasecmp(
2284
                $a['Display_Name'],
2285
                $b['Display_Name']
2286
            );
2287
        });
2288
2289
        return $retarray;
2290
    }
2291
2292
    /**
2293
     * printAttributeReleaseErrorMessage
2294
     *
2295
     * This is a convenience method called by handleGotUser to print out
2296
     * the attribute release error page to the user.
2297
     *
2298
     * @param string $ePPN
2299
     * @param string $ePTID
2300
     * @param string $firstname
2301
     * @param string $lastname
2302
     * @param string $displayname
2303
     * @param string $emailaddr
2304
     * @param string $idp
2305
     * @param string $idpname
2306
     * @param string $affiliation
2307
     * @param string $ou
2308
     * @param string $memberof
2309
     * @param string $acr
2310
     * @param string $entitlement
2311
     * @param string $itrustuin
2312
     * @param string $clientparams
2313
     * @param string $redirect
2314
     * @param string $redirectform
2315
     * @param bool   $edugainandgetcert
2316
     */
2317
    public static function printAttributeReleaseErrorMessage(
2318
        $ePPN,
2319
        $ePTID,
2320
        $firstname,
2321
        $lastname,
2322
        $displayname,
2323
        $emailaddr,
2324
        $idp,
2325
        $idpname,
2326
        $affiliation,
2327
        $ou,
0 ignored issues
show
Unused Code introduced by
The parameter $ou is not used and could be removed. ( Ignorable by Annotation )

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

2327
        /** @scrutinizer ignore-unused */ $ou,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2328
        $memberof,
0 ignored issues
show
Unused Code introduced by
The parameter $memberof is not used and could be removed. ( Ignorable by Annotation )

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

2328
        /** @scrutinizer ignore-unused */ $memberof,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2329
        $acr,
0 ignored issues
show
Unused Code introduced by
The parameter $acr is not used and could be removed. ( Ignorable by Annotation )

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

2329
        /** @scrutinizer ignore-unused */ $acr,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2330
        $entitlement,
0 ignored issues
show
Unused Code introduced by
The parameter $entitlement is not used and could be removed. ( Ignorable by Annotation )

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

2330
        /** @scrutinizer ignore-unused */ $entitlement,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2331
        $itrustuin,
0 ignored issues
show
Unused Code introduced by
The parameter $itrustuin is not used and could be removed. ( Ignorable by Annotation )

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

2331
        /** @scrutinizer ignore-unused */ $itrustuin,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2332
        $clientparams,
0 ignored issues
show
Unused Code introduced by
The parameter $clientparams is not used and could be removed. ( Ignorable by Annotation )

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

2332
        /** @scrutinizer ignore-unused */ $clientparams,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2333
        $redirect,
2334
        $redirectform,
2335
        $edugainandgetcert
2336
    ) {
2337
        $errorboxstr =
2338
        '<p>There was a problem logging on. Your identity
2339
        provider has not provided CILogon with required information.</p>
2340
        <blockquote><table cellpadding="5">';
2341
2342
        $missingattrs = '';
2343
        // Show user which attributes are missing
2344
        if ((strlen($ePPN) == 0) && (strlen($ePTID) == 0)) {
2345
            $errorboxstr .=
2346
            '<tr><th>ePTID:</th><td>MISSING</td></tr>
2347
            <tr><th>ePPN:</th><td>MISSING</td></tr>';
2348
            $missingattrs .= '%0D%0A    eduPersonPrincipalName' .
2349
                             '%0D%0A    eduPersonTargetedID ';
2350
        }
2351
        if ((strlen($firstname) == 0) && (strlen($displayname) == 0)) {
2352
            $errorboxstr .=
2353
            '<tr><th>First Name:</th><td>MISSING</td></tr>';
2354
            $missingattrs .= '%0D%0A    givenName (first name)';
2355
        }
2356
        if ((strlen($lastname) == 0) && (strlen($displayname) == 0)) {
2357
            $errorboxstr .=
2358
            '<tr><th>Last Name:</th><td>MISSING</td></tr>';
2359
            $missingattrs .= '%0D%0A    sn (last name)';
2360
        }
2361
        if (
2362
            (strlen($displayname) == 0) &&
2363
            ((strlen($firstname) == 0) || (strlen($lastname) == 0))
2364
        ) {
2365
            $errorboxstr .=
2366
            '<tr><th>Display Name:</th><td>MISSING</td></tr>';
2367
            $missingattrs .= '%0D%0A    displayName';
2368
        }
2369
        $emailvalid = filter_var($emailaddr, FILTER_VALIDATE_EMAIL);
2370
        if ((strlen($emailaddr) == 0) || (!$emailvalid)) {
2371
            $errorboxstr .=
2372
            '<tr><th>Email Address:</th><td>' .
2373
            ((strlen($emailaddr) == 0) ? 'MISSING' : 'INVALID') .
2374
            '</td></tr>';
2375
            $missingattrs .= '%0D%0A    mail (email address)';
2376
        }
2377
        // CIL-326/CIL-539 - For eduGAIN IdPs attempting to get a cert,
2378
        // print out missing R&S and SIRTFI values
2379
        $idplist = Util::getIdpList();
2380
        if ($edugainandgetcert) {
2381
            if (!$idplist->isREFEDSRandS($idp)) {
2382
                $errorboxstr .=
2383
                '<tr><th><a target="_blank"
2384
                href="http://refeds.org/category/research-and-scholarship">Research
2385
                and Scholarship</a>:</th><td>MISSING</td></tr>';
2386
                $missingattrs .= '%0D%0A    http://refeds.org/category/research-and-scholarship';
2387
            }
2388
            if (!$idplist->isSIRTFI($idp)) {
2389
                $errorboxstr .=
2390
                '<tr><th><a target="_blank"
2391
                href="https://refeds.org/sirtfi">SIRTFI</a>:</th><td>MISSING</td></tr>';
2392
                $missingattrs .= '%0D%0A    http://refeds.org/sirtfi';
2393
            }
2394
        }
2395
        $student = false;
2396
        $errorboxstr .= '</table></blockquote>';
2397
        if (
2398
            (strlen($emailaddr) == 0) &&
2399
            (preg_match('/student@/', $affiliation))
2400
        ) {
2401
            $student = true;
2402
            $errorboxstr .= '<p><b>If you are a student</b>, ' .
2403
            'you may need to ask your identity provider ' .
2404
            'to release your email address.</p>';
2405
        }
2406
2407
        // Get contacts from metadata for email addresses
2408
        $shibarray = $idplist->getShibInfo($idp);
2409
        $emailmsg = '?subject=Attribute Release Problem for CILogon' .
2410
        '&[email protected]' .
2411
        '&body=Hello, I am having trouble logging on to ' .
2412
        'https://' . DEFAULT_HOSTNAME . '/ using the ' . $idpname .
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_HOSTNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2413
        ' Identity Provider (IdP) ' .
2414
        'due to the following missing attributes:%0D%0A' .
2415
        $missingattrs;
2416
        if ($student) {
2417
            $emailmsg .= '%0D%0A%0D%0ANote that my account is ' .
2418
            'marked "student" and thus my email address may need ' .
2419
            'to be released.';
2420
        }
2421
        $emailmsg .= '%0D%0A%0D%0APlease see ' .
2422
            'http://www.cilogon.org/service/addidp for more ' .
2423
            'details. Thank you for any help you can provide.';
2424
        $errorboxstr .= '<p>Contact your identity provider to ' .
2425
        'let them know you are having having a problem logging on ' .
2426
        'to CILogon.</p><blockquote><ul>';
2427
2428
        $addrfound = false;
2429
        $name = @$shibarray['Support Name'];
2430
        $addr = @$shibarray['Support Address'];
2431
        $addr = preg_replace('/^mailto:/', '', $addr);
2432
2433
        if (strlen($addr) > 0) {
2434
            $addrfound = true;
2435
            if (strlen($name) == 0) { // Use address if no name given
2436
                $name = $addr;
2437
            }
2438
            $errorboxstr .= '<li> Support Contact: ' .
2439
                $name . ' &lt;<a href="mailto:' .
2440
                $addr . $emailmsg . '">' .
2441
                $addr . '</a>&gt;</li>';
2442
        }
2443
2444
        if (!$addrfound) {
2445
            $name = @$shibarray['Technical Name'];
2446
            $addr = @$shibarray['Technical Address'];
2447
            $addr = preg_replace('/^mailto:/', '', $addr);
2448
            if (strlen($addr) > 0) {
2449
                $addrfound = true;
2450
                if (strlen($name) == 0) { // Use address if no name given
2451
                    $name = $addr;
2452
                }
2453
                $errorboxstr .= '<li> Technical Contact: ' .
2454
                    $name . ' &lt;<a href="mailto:' .
2455
                    $addr . $emailmsg . '">' .
2456
                    $addr . '</a>&gt;</li>';
2457
            }
2458
        }
2459
2460
        if (!$addrfound) {
2461
            $name = @$shibarray['Administrative Name'];
2462
            $addr = @$shibarray['Administrative Address'];
2463
            $addr = preg_replace('/^mailto:/', '', $addr);
2464
            if (strlen($addr) > 0) {
2465
                if (strlen($name) == 0) { // Use address if no name given
2466
                    $name = $addr;
2467
                }
2468
                $errorboxstr .= '<li>Administrative Contact: ' .
2469
                    $name . ' &lt;<a href="mailto:' .
2470
                    $addr . $emailmsg . '">' .
2471
                    $addr . '</a>&gt</li>';
2472
            }
2473
        }
2474
2475
        $errorboxstr .= '</ul></blockquote>
2476
2477
        <p> Alternatively, you can contact us at the email address
2478
        at the bottom of the page.</p>
2479
        ';
2480
2481
        static::printErrorBox($errorboxstr);
2482
2483
        echo '
2484
        <div>
2485
        ';
2486
2487
        static::printFormHead($redirect, 'get');
2488
        echo $redirectform , '
2489
        <input type="submit" name="submit" class="submit"
2490
        value="Proceed" />
2491
        </form>
2492
        </div>
2493
        ';
2494
    }
2495
2496
    /**
2497
     * getIdphintList
2498
     *
2499
     * This function adds support for AARC-G049 "IdP Hinting". It
2500
     * searches both the GET query parameters and the OIDC client
2501
     * parameters passed to the 'authorize' endpoint for a parameter
2502
     * named either 'selected_idp' or 'idphint'. This parameter can be
2503
     * a single entityId or a comma-separated list of entityIds.
2504
     * The entries in the list are processed to remove any 'chained'
2505
     * idphints and also to transform OIDC 'issuer' values into
2506
     * CILogon-specific 'entityIds' as used in the 'Select an IdP'
2507
     * list. Any idps which are not in the current skin's 'Select
2508
     * an IdP' list are removed. The resulting processed list of
2509
     * entityIds is returned, which may be an empty array.
2510
     *
2511
     * @param array $idps (Optional) A list of valid (i.e., whitelisted) IdPs.
2512
     *        If this list is empty, then use the current skin's IdP list.
2513
     * @return array A list of entityIds / OIDC provider URLs extracted from
2514
     *         a passed-in parameter 'selected_idp' or 'idphint'. This array
2515
     *         may be empty if no such parameter was found, or if the
2516
     *         entityIds in the list were not valid.
2517
     */
2518
    public static function getIdphintList($idps = [])
2519
    {
2520
        // Check for either 'selected_idp' or 'idphint' parameter that was
2521
        // passed in via a query parameter, either for an OAuth transaction
2522
        // or just 'normally'. Note that if both 'selected_idp' and
2523
        // 'idphint' were passed, 'idphint' takes priority.
2524
2525
        $hintarray = array();
2526
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2527
2528
        $hintstr = '';
2529
        if (!empty(@$clientparams['idphint'])) {
2530
            $hintstr = $clientparams['idphint'];
2531
        } elseif (!empty(Util::getGetVar('idphint'))) {
2532
            $hintstr = Util::getGetVar('idphint');
2533
        } elseif (!empty(@$clientparams['selected_idp'])) {
2534
            $hintstr = $clientparams['selected_idp'];
2535
        } elseif (!empty(Util::getGetVar('selected_idp'))) {
2536
            $hintstr = Util::getGetVar('selected_idp');
2537
        }
2538
2539
        if (!empty($hintstr)) {
2540
            // Split on comma to account for multiple idps
2541
            $hintarray = explode(',', $hintstr);
2542
2543
            // Process the list of IdPs to transform them appropriately.
2544
            foreach ($hintarray as &$value) {
2545
                // Check for 'chained' idp hints, and remove the GET params.
2546
                if (preg_match('%([^\?]*)\?%', $value, $matches)) {
2547
                    $value = $matches[1];
2548
                }
2549
                // Also, check for OIDC issuers and transform them into
2550
                // CILogon-specific values used in the 'Select an IdP' list.
2551
                if (preg_match('%https://accounts.google.com%', $value)) {
2552
                    $value = 'https://accounts.google.com/o/oauth2/auth';
2553
                } elseif (preg_match('%https://github.com%', $value)) {
2554
                    $value = 'https://github.com/login/oauth/authorize';
2555
                } elseif (preg_match('%https://orcid.org%', $value)) {
2556
                    $value = 'https://orcid.org/oauth/authorize';
2557
                }
2558
            }
2559
            unset($value); // Break the reference with the last element.
2560
2561
            // Remove any non-whitelisted IdPs from the hintarray.
2562
            if (empty($idps)) {
2563
                $idps = static::getCompositeIdPList();
2564
            }
2565
            foreach ($hintarray as $value) {
2566
                if (!isset($idps[$value])) {
2567
                    if (($key = array_search($value, $hintarray)) !== false) {
2568
                        unset($hintarray[$key]);
2569
                    }
2570
                }
2571
            }
2572
        }
2573
        return $hintarray;
2574
    }
2575
}
2576