Passed
Push — master ( f95214...fe518b )
by Terrence
14:22
created

Content::printTwoFactorPage()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 158
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 46
nc 8
nop 0
dl 0
loc 158
ccs 0
cts 140
cp 0
crap 72
rs 7.9337
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
F Content::handleNoSubmitButtonClicked() 0 137 29

How to fix   Long Method   

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
// If needed, set the 'Notification' banner text to a non-empty value
14
// and uncomment the 'define' statement in order to display a
15
// notification box at the top of each page.
16
/*
17
define('BANNER_TEXT',
18
       'We are currently experiencing problems issuing certificates. We are
19
       working on a solution. We apologize for the inconvenience.'
20
);
21
*/
22
23
/**
24
 * Content
25
 */
26
class Content
27
{
28
    /**
29
     * printHeader
30
     *
31
     * This function should be called to print out the main HTML header
32
     * block for each web page.  This gives a consistent look to the site.
33
     * Any style changes should go in the cilogon.css file.
34
     *
35
     * @param string $title The text in the window's titlebar
36
     * @param string $extra Optional extra text to go in the <head> block
37
     * @param bool $csrfcookie Set the CSRF cookie. Defaults to true.
38
     */
39
    public static function printHeader($title = '', $extra = '', $csrfcookie = true)
40
    {
41
        if ($csrfcookie) {
42
            $csrf = Util::getCsrf();
43
            $csrf->setTheCookie();
44
        }
45
46
        // Find the 'Powered By CILogon' image if specified by the skin
47
        $poweredbyimg = "/images/poweredbycilogon.png";
48
        $skin = Util::getSkin();
49
        $skinpoweredbyimg = (string)$skin->getConfigOption('poweredbyimg');
50
        if (
51
            (!is_null($skinpoweredbyimg)) &&
0 ignored issues
show
introduced by
The condition is_null($skinpoweredbyimg) is always false.
Loading history...
52
            (strlen($skinpoweredbyimg) > 0) &&
53
            (is_readable('/var/www/html' . $skinpoweredbyimg))
54
        ) {
55
            $poweredbyimg = $skinpoweredbyimg;
56
        }
57
58
        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
59
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
60
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
61
        <head><title>' , $title , '</title>
62
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
63
        <meta name="viewport" content="initial-scale=0.6" />
64
        <link rel="stylesheet" type="text/css" href="/include/cilogon.css" />
65
        ';
66
67
        $skin->printSkinLink();
68
69
        $deployjava = $skin->getConfigOption('deployjava');
70
        if ((!is_null($deployjava)) && ((int)$deployjava == 1)) {
71
            echo '<script type="text/javascript" src="/include/deployJava.js"></script>';
72
        }
73
74
        echo '
75
        <script type="text/javascript" src="/include/cilogon.js"></script>
76
        ' ;
77
78
        echo '
79
    <!--[if IE]>
80
        <style type="text/css">
81
          body { behavior: url(/include/csshover3.htc); }
82
        </style>
83
    <![endif]-->
84
        ';
85
86
        if (strlen($extra) > 0) {
87
            echo $extra;
88
        }
89
90
        echo '
91
        </head>
92
93
        <body>
94
95
        <div class="skincilogonlogo">
96
        <a target="_blank" href="http://www.cilogon.org/faq/"><img
97
        src="' , $poweredbyimg , '" alt="CILogon"
98
        title="CILogon Service" /></a>
99
        </div>
100
101
        <div class="logoheader">
102
           <h1><span>[CILogon Service]</span></h1>
103
        </div>
104
        <div class="pagecontent">
105
         ';
106
107
        if ((defined('BANNER_TEXT')) && (strlen(BANNER_TEXT) > 0)) {
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...
108
            echo '
109
            <div class="noticebanner">' , BANNER_TEXT , '</div>
110
            ';
111
        }
112
113
        $providerId = Util::getSessionVar('idp');
114
        if ($providerId == "urn:mace:incommon:idp.protectnetwork.org") {
115
            echo '
116
            <div class="noticebanner">Availability of the ProtectNetwork
117
            Identity Provider (IdP) will end after December 2014. Please
118
            consider using another IdP.</div>
119
            ';
120
        }
121
    }
122
123
    /**
124
     * printFooter
125
     *
126
     * This function should be called to print out the closing HTML block
127
     * for each web page.
128
     *
129
     * @param string $footer Optional extra text to be output before the
130
     * closing footer div.
131
     */
132
    public static function printFooter($footer = '')
133
    {
134
        if (strlen($footer) > 0) {
135
            echo $footer;
136
        }
137
138
        echo '
139
        <br class="clear" />
140
        <div class="footer">
141
        <a target="_blank" href="http://www.cilogon.org/faq"><img
142
        src="/images/questionIcon.png" class="floatrightclear"
143
        width="40" height="40" alt="CILogon FAQ" title="CILogon FAQ" /></a>
144
        <p>For questions about this site, please see the <a target="_blank"
145
        href="http://www.cilogon.org/faq">FAQs</a> or send email to <a
146
        href="mailto:[email protected]">help&nbsp;@&nbsp;cilogon.org</a>.</p>
147
        <p>Know <a target="_blank"
148
        href="http://ca.cilogon.org/responsibilities">your responsibilities</a>
149
        for using the CILogon Service.</p>
150
        <p>See <a target="_blank"
151
        href="http://ca.cilogon.org/acknowledgements">acknowledgements</a> of
152
        support for this site.</p>
153
        </div> <!-- Close "footer" div -->
154
        </div> <!-- Close "pagecontent" div -->
155
        </body>
156
        </html>
157
        ';
158
159
        session_write_close();
160
    }
161
162
    /**
163
     * printPageHeader
164
     *
165
     * This function prints a fancy formatted box with a single line of
166
     * text, suitable for a titlebox on each web page (to appear just below
167
     * the page banner at the very top). It prints a gradent border around
168
     * the four edges of the box and then outlines the inner box.
169
     *
170
     * @param string $text The text string to appear in the titlebox.
171
     */
172
    public static function printPageHeader($text)
173
    {
174
        echo '
175
        <div class="titlebox">' , $text , '
176
        </div>
177
        ';
178
    }
179
180
    /**
181
     * printFormHead
182
     *
183
     * This function prints out the opening <form> tag for displaying
184
     * submit buttons.  The first parameter is used for the 'action' value
185
     * of the <form>.  If omitted, getScriptDir() is called to get the
186
     * location of the current script.  This function outputs a hidden csrf
187
     * field in the form block.
188
     *
189
     * @param string $action (Optional) The value of the form's 'action'
190
     *        parameter. Defaults to getScriptDir().
191
     * @param string $method (Optional) The <form> 'method', one of 'get' or
192
     *        'post'. Defaults to 'post'.
193
     */
194
    public static function printFormHead(
195
        $action = '',
196
        $method = 'post'
197
    ) {
198
        static $formnum = 0;
199
200
        if (strlen($action) == 0) {
201
            $action = Util::getScriptDir();
202
        }
203
204
        echo '
205
        <form action="' , $action , '" method="' , $method , '"
206
         autocomplete="off" id="form' , sprintf("%02d", ++$formnum) , '">
207
        ';
208
        $csrf = Util::getCsrf();
209
        echo $csrf->hiddenFormElement();
210
    }
211
212
    /**
213
     * printWAYF
214
     *
215
     * This function prints the list of IdPs in a <select> form element
216
     * which can be printed on the main login page to allow the user to
217
     * select 'Where Are You From?'.  This function checks to see if a
218
     * cookie for the 'providerId' had been set previously, so that the
219
     * last used IdP is selected in the list.
220
     *
221
     * @param bool $showremember (Optional) Show the 'Remember this
222
     *        selection' checkbox? Defaults to true.
223
     * @param bool $samlidps (Optional) Show all SAML-based IdPs in
224
     *        selection list? Defaults to false, which means show
225
     *        only whitelisted IdPs.
226
     */
227
    public static function printWAYF($showremember = true, $samlidps = false)
228
    {
229
        $helptext = 'Check this box to bypass the welcome page on ' .
230
            'subsequent visits and proceed directly to the selected ' .
231
            'identity provider. You will need to clear your browser\'s ' .
232
            'cookies to return here.';
233
        $searchtext = "Enter characters to search for in the list above.";
234
235
        // Get an array of IdPs
236
        $idps = static::getCompositeIdPList($samlidps);
237
238
        $skin = Util::getSkin();
239
240
        // Check if the user had previously selected an IdP from the list.
241
        // First, check the portalcookie, then the 'normal' cookie.
242
        $keepidp = '';
243
        $providerId = '';
244
        $pc = new PortalCookie();
245
        $pn = $pc->getPortalName();
246
        if (strlen($pn) > 0) {
247
            $keepidp    = $pc->get('keepidp');
248
            $providerId = $pc->get('providerId');
249
        } else {
250
            $keepidp    = Util::getCookieVar('keepidp');
251
            $providerId = Util::getCookieVar('providerId');
252
        }
253
254
        // Make sure previously selected IdP is in list of available IdPs.
255
        if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
256
            $providerId = '';
257
        }
258
259
        // If no previous providerId, get from skin, or default to Google.
260
        if (strlen($providerId) == 0) {
261
            $initialidp = (string)$skin->getConfigOption('initialidp');
262
            if ((!is_null($initialidp)) && (isset($idps[$initialidp]))) {
0 ignored issues
show
introduced by
The condition is_null($initialidp) is always false.
Loading history...
263
                $providerId = $initialidp;
264
            } else {
265
                $providerId = Util::getAuthzUrl('Google');
266
            }
267
        }
268
269
        // Check if an OIDC client selected an IdP for the transaction.
270
        // If so, verify that the IdP is in the list of available IdPs.
271
        $useselectedidp = false;
272
        $idphintlist = static::getIdphintList($idps);
273
        if (!empty($idphintlist)) {
274
            $useselectedidp = true;
275
            $providerId = $idphintlist[0];
276
            // Update the IdP selection list to show just the idphintlist.
277
            foreach ($idphintlist as $value) {
278
                $newidps[$value] = $idps[$value];
279
            }
280
            $idps = $newidps;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $newidps seems to be defined by a foreach iteration on line 277. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
281
            // Re-sort the $idps by Display_Name for correct alphabetization.
282
            uasort($idps, function ($a, $b) {
283
                return strcasecmp(
284
                    $a['Display_Name'],
285
                    $b['Display_Name']
286
                );
287
            });
288
        }
289
290
        echo '
291
        <br />
292
        <div class="actionbox"';
293
294
        if (Util::getSessionVar('showhelp') == 'on') {
295
            echo ' style="width:92%;"';
296
        }
297
298
        echo '>
299
        <table class="helptable">
300
        <tr>
301
        <td class="actioncell">
302
303
          <form action="' , Util::getScriptDir() , '" method="post">
304
          <fieldset>
305
306
          <p>' , ($useselectedidp ? 'Selected' : 'Select An') ,
307
          ' Identity Provider:</p>
308
          ';
309
310
        // See if the skin has set a size for the IdP <select> list
311
        $selectsize = 4;
312
        $ils = $skin->getConfigOption('idplistsize');
313
        if ((!is_null($ils)) && ((int)$ils > 0)) {
314
            $selectsize = (int)$ils;
315
        }
316
317
        // When selected_idp is used, list size may be smaller
318
        if ($useselectedidp) {
319
            $selectsize = min($selectsize, count($idps));
320
        }
321
322
        echo '
323
          <p>
324
          <select name="providerId" id="providerId" size="' , $selectsize , '"
325
           onkeypress="enterKeySubmit(event)" ondblclick="doubleClickSubmit()"' ,
326
           // Hide the drop-down arrow in Firefox and Chrome
327
          ($useselectedidp ?
328
              'style="-moz-appearance:none;-webkit-appearance:none"' : '') ,
329
           '>
330
        ';
331
332
        foreach ($idps as $entityId => $names) {
333
            echo '    <option value="' , $entityId , '"';
334
            if ($entityId == $providerId) {
335
                echo ' selected="selected"';
336
            }
337
            echo '>' , Util::htmlent($names['Display_Name']) , '</option>' , "\n    ";
338
        }
339
340
        echo '  </select>
341
        </p>
342
343
        <p id="listsearch" class="zeroheight">
344
        <label for="searchlist" class="helpcursor" title="' ,
345
        $searchtext , '">Search:</label>
346
        <input type="text" name="searchlist" id="searchlist" value=""
347
        size="30" onkeyup="searchOptions(this.value)"
348
        title="' , $searchtext , '" />
349
    <!--[if IE]><input type="text" style="display:none;" disabled="disabled" size="1"/><![endif]-->
350
        </p>
351
        ';
352
353
        if ($showremember) {
354
            echo '
355
            <p>
356
            <label for="keepidp" title="' , $helptext ,
357
            '" class="helpcursor">Remember this selection:</label>
358
            <input type="checkbox" name="keepidp" id="keepidp" ' ,
359
            ((strlen($keepidp) > 0) ? 'checked="checked" ' : '') ,
360
            'title="' , $helptext , '" class="helpcursor" />
361
            </p>
362
            ';
363
        }
364
365
        echo '
366
        <p class="silvercheckbox">
367
        <label for="silveridp">Request Silver:</label>
368
        <input type="checkbox" name="silveridp" id="silveridp"/>
369
        </p>
370
371
        <p>
372
        ';
373
374
        echo Util::getCsrf()->hiddenFormElement();
375
376
        $lobtext = static::getLogOnButtonText();
377
378
        echo '
379
        <input type="submit" name="submit" class="submit helpcursor"
380
        title="Continue to the selected identity provider."
381
        value="' , $lobtext , '" id="wayflogonbutton" />
382
        <input type="hidden" name="previouspage" value="WAYF" />
383
        <input type="submit" name="submit" class="submit helpcursor"
384
        title="Cancel authentication and navigate away from this site."
385
        value="Cancel" id="wayfcancelbutton" />
386
        </p>
387
        ';
388
389
        $logonerror = Util::getSessionVar('logonerror');
390
        if (strlen($logonerror) > 0) {
391
            echo "<p class=\"logonerror\">$logonerror</p>";
392
            Util::unsetSessionVar('logonerror');
393
        }
394
395
        echo '
396
        <p class="privacypolicy">
397
        By selecting "' , $lobtext , '", you agree to <a target="_blank"
398
        href="http://ca.cilogon.org/policy/privacy">CILogon\'s privacy
399
        policy</a>.
400
        </p>
401
402
        </fieldset>
403
404
        </form>
405
      </td>
406
      ';
407
408
        if (Util::getSessionVar('showhelp') == 'on') {
409
            echo '
410
          <td class="helpcell">
411
          <div>
412
          ';
413
414
            if ($samlidps) { // SAML-based IdPs only means running from /testidp/
415
                echo '
416
                <p>
417
                CILogon facilitates secure access to CyberInfrastructure
418
                (<acronym title="CyberInfrastructure">CI</acronym>). In
419
                order to test your identity provider with the CILogon Service,
420
                you must first Log On. If your preferred identity provider is
421
                not listed, please contact <a
422
                href="mailto:[email protected]">[email protected]</a>, and
423
                we will try to add your identity provider in the future.
424
                </p>
425
                ';
426
            } else { // If not InCommon only, print help text for OpenID providers.
427
                echo '
428
                <p>
429
                CILogon facilitates secure access to CyberInfrastructure
430
                (<acronym title="CyberInfrastructure">CI</acronym>).
431
                In order to use the CILogon Service, you must first select
432
                an identity provider. An identity provider (IdP) is an
433
                organization where you have an account and can log on
434
                to gain access to online services.
435
                </p>
436
                <p>
437
                If you are a faculty, staff, or student member of a university
438
                or college, please select it for your identity provider.
439
                If your school is not listed, please contact <a
440
                href="mailto:[email protected]">[email protected]</a>, and we will
441
                try to add your school in the future.
442
                </p>
443
                ';
444
445
                $googleauthz = Util::getAuthzUrl('Google');
446
                if (
447
                    (isset($idps[$googleauthz])) &&
448
                    ($skin->idpAvailable($googleauthz))
449
                ) {
450
                    echo '
451
                  <p>
452
                  If you have a <a target="_blank"
453
                  href="https://myaccount.google.com">Google</a>
454
                  account, you can select it for
455
                  authenticating to the CILogon Service.
456
                  </p>
457
                  ';
458
                }
459
                $githubauthz = Util::getAuthzUrl('GitHub');
460
                if (
461
                    (isset($idps[$githubauthz])) &&
462
                    ($skin->idpAvailable($githubauthz))
463
                ) {
464
                    echo '
465
                  <p>
466
                  If you have a <a target="_blank"
467
                  href="https://github.com/settings/profile">GitHub</a>
468
                  account, you can select it for
469
                  authenticating to the CILogon Service.
470
                  </p>
471
                  ';
472
                }
473
                $orcidauthz = Util::getAuthzUrl('ORCID');
474
                if (
475
                    (isset($idps[$orcidauthz])) &&
476
                    ($skin->idpAvailable($orcidauthz))
477
                ) {
478
                    echo '
479
                  <p>
480
                  If you have a <a target="_blank"
481
                  href="https://orcid.org/my-orcid">ORCID</a>
482
                  account, you can select it for
483
                  authenticating to the CILogon Service.
484
                  </p>
485
                  ';
486
                }
487
            }
488
489
            echo '
490
          </div>
491
          </td>
492
          ';
493
        }
494
        echo '
495
      </tr>
496
      </table>
497
      </div>
498
      ';
499
    }
500
501
    /**
502
     * handleLogOnButtonClicked
503
     *
504
     * This function is called when the user clicks the 'Log On' button
505
     * on the IdP selection page. It checks to see if the 'Remember this
506
     * selection' checkbox was checked and sets a cookie appropriately. It
507
     * also sets a cookie 'providerId' so the last chosen IdP will be
508
     * selected the next time the user visits the site. The function then
509
     * calls the appropriate 'redirectTo...' function to send the user
510
     * to the chosen IdP.
511
     */
512
    public static function handleLogOnButtonClicked()
513
    {
514
        // Get the list of currently available IdPs
515
        $idps = static::getCompositeIdPList();
516
517
        // Set the cookie for keepidp if the checkbox was checked
518
        $pc = new PortalCookie();
519
        $pn = $pc->getPortalName();
520
        if (strlen(Util::getPostVar('keepidp')) > 0) {
521
            if (strlen($pn) > 0) {
522
                $pc->set('keepidp', 'checked');
523
            } else {
524
                Util::setCookieVar('keepidp', 'checked');
525
            }
526
        } else {
527
            if (strlen($pn) > 0) {
528
                $pc->set('keepidp', '');
529
            } else {
530
                Util::unsetCookieVar('keepidp');
531
            }
532
        }
533
534
        // Get the user-chosen IdP from the posted form
535
        $providerId = Util::getPostVar('providerId');
536
537
        // Set the cookie for the last chosen IdP and redirect to it if in list
538
        if ((strlen($providerId) > 0) && (isset($idps[$providerId]))) {
539
            if (strlen($pn) > 0) {
540
                $pc->set('providerId', $providerId);
541
                $pc->write();
542
            } else {
543
                Util::setCookieVar('providerId', $providerId);
544
            }
545
            $providerName = Util::getAuthzIdP($providerId);
546
            if (in_array($providerName, Util::$oauth2idps)) {
547
                // Log in with an OAuth2 IdP
548
                static::redirectToGetOAuth2User($providerId);
549
            } else { // Use InCommon authn
550
                static::redirectToGetShibUser($providerId);
551
            }
552
        } else { // IdP not in list, or no IdP selected
553
            if (strlen($pn) > 0) {
554
                $pc->set('providerId', '');
555
                $pc->write();
556
            } else {
557
                Util::unsetCookieVar('providerId');
558
            }
559
            Util::setSessionVar('logonerror', 'Please select a valid IdP.');
560
            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

560
            /** @scrutinizer ignore-call */ 
561
            printLogonPage();
Loading history...
561
        }
562
    }
563
564
    /**
565
     * handleHelpButtonClicked
566
     *
567
     * This function is called when the user clicks on the 'Show Help' /
568
     * 'Hide Help' button in the upper right corner of the page. It toggles
569
     * the 'showhelp' session variable and redisplays the appropriate page
570
     * with help now shown or hidden.
571
     */
572
    public static function handleHelpButtonClicked()
573
    {
574
        if (Util::getSessionVar('showhelp') == 'on') {
575
            Util::unsetSessionVar('showhelp');
576
        } else {
577
            Util::setSessionVar('showhelp', 'on');
578
        }
579
580
        $stage = Util::getSessionVar('stage');
581
        if (static::verifyCurrentUserSession()) {
582
            if ($stage == 'main') {
583
                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

583
                /** @scrutinizer ignore-call */ 
584
                printMainPage();
Loading history...
584
            } else {
585
                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

585
                /** @scrutinizer ignore-call */ 
586
                printLogonPage();
Loading history...
586
            }
587
        } else {
588
            printLogonPage();
589
        }
590
    }
591
592
    /**
593
     * handleNoSubmitButtonClicked
594
     *
595
     * This function is the 'default' case when no 'submit' button has been
596
     * clicked, or if the submit session variable is not set. It checks
597
     * to see if either the <forceinitialidp> option is set, or if the
598
     * 'Remember this selection' checkbox was previously checked. If so,
599
     * then rediret to the appropriate IdP. Otherwise, print the main
600
     * Log On page.
601
     */
602
    public static function handleNoSubmitButtonClicked()
603
    {
604
        $providerId = '';
605
        $keepidp = '';
606
        $selected_idp = '';
607
        $redirect_uri = '';
608
        $client_id = '';
609
        $callbackuri = Util::getSessionVar('callbackuri');
610
        $readidpcookies = true;  // Assume config options are not set
611
        $skin = Util::getSkin();
612
        $forceinitialidp = (int)$skin->getConfigOption('forceinitialidp');
613
        $initialidp = (string)$skin->getConfigOption('initialidp');
614
615
        // If this is a OIDC transaction, get the redirect_uri and
616
        // client_id parameters from the session var clientparams.
617
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
618
        if (isset($clientparams['redirect_uri'])) {
619
            $redirect_uri = $clientparams['redirect_uri'];
620
        }
621
        if (isset($clientparams['client_id'])) {
622
            $client_id = $clientparams['client_id'];
623
        }
624
625
        // Use the first element of the idphint list as the selected_idp.
626
        $idphintlist = static::getIdphintList();
627
        if (!empty($idphintlist)) {
628
            $selected_idp = $idphintlist[0];
629
        }
630
631
        // CIL-431 - If the OAuth2/OIDC $redirect_uri or $client_id is set,
632
        // then check for a match in the 'bypass.txt' file to see if we
633
        // should automatically redirect to a specific IdP. Used mainly
634
        // by campus gateways.
635
        if ((strlen($redirect_uri) > 0) || (strlen($client_id) > 0)) {
636
            $bypassidp = '';
637
            $bypassarray = Util::readArrayFromFile(
638
                Util::getServerVar('DOCUMENT_ROOT') . '/include/bypass.txt'
639
            );
640
            foreach ($bypassarray as $key => $value) {
641
                if (
642
                    (preg_match($key, $redirect_uri)) ||
643
                    (preg_match($key, $client_id))
644
                ) {
645
                    $bypassidp = $value;
646
                    break;
647
                }
648
            }
649
            if (strlen($bypassidp) > 0) { // Match found!
650
                $providerId = $bypassidp;
651
                $keepidp = 'checked';
652
                // To skip the next code blocks, unset a few variables.
653
                $forceinitialidp = 0;     // Skip checking this option
654
                $selected_idp = '';       // Skip any passed-in option
655
                $readidpcookies = false;  // Don't read in the IdP cookies
656
            }
657
        }
658
659
        // If the <forceinitialidp> option is set, use either the
660
        // <initialidp> or the selected_idp as the providerId, and use
661
        // <forceinitialidp> as keepIdp. Otherwise, read the cookies
662
        // 'providerId' and 'keepidp'.
663
        if (
664
            ($forceinitialidp == 1) &&
665
            ((strlen($initialidp) > 0) || (strlen($selected_idp) > 0))
666
        ) {
667
            // If the <allowforceinitialidp> option is set, then make sure
668
            // the callback / redirect uri is in the portal list.
669
            $afii = $skin->getConfigOption('portallistaction', 'allowforceinitialidp');
670
            if (
671
                (is_null($afii)) || // Option not set, no need to check portal list
672
                (((int)$afii == 1) &&
673
                  (($skin->inPortalList($redirect_uri)) ||
674
                   ($skin->inPortalList($client_id)) ||
675
                   ($skin->inPortalList($callbackuri))))
676
            ) {
677
                // 'selected_idp' takes precedence over <initialidp>
678
                if (strlen($selected_idp) > 0) {
679
                    $providerId = $selected_idp;
680
                } else {
681
                    $providerId = $initialidp;
682
                }
683
                $keepidp = $forceinitialidp;
684
                $readidpcookies = false; // Don't read in the IdP cookies
685
            }
686
        }
687
688
        // <initialidp> options not set, or portal not in portal list?
689
        // Get idp and 'Remember this selection' from cookies instead.
690
        $pc = new PortalCookie();
691
        $pn = $pc->getPortalName();
692
        if ($readidpcookies) {
693
            // Check the portalcookie first, then the 'normal' cookies
694
            if (strlen($pn) > 0) {
695
                $keepidp    = $pc->get('keepidp');
696
                $providerId = $pc->get('providerId');
697
            } else {
698
                $keepidp    = Util::getCookieVar('keepidp');
699
                $providerId = Util::getCookieVar('providerId');
700
            }
701
        }
702
703
        // If both 'keepidp' and 'providerId' were set (and the
704
        // providerId is a whitelisted IdP or valid OpenID provider),
705
        // then skip the Logon page and proceed to the appropriate
706
        // getuser script.
707
        if ((strlen($providerId) > 0) && (strlen($keepidp) > 0)) {
708
            // If selected_idp was specified at the OIDC authorize endpoint,
709
            // make sure that it matches the saved providerId. If not,
710
            // then show the Logon page and uncheck the keepidp checkbox.
711
            if ((strlen($selected_idp) == 0) || ($selected_idp == $providerId)) {
712
                $providerName = Util::getAuthzIdP($providerId);
713
                if (in_array($providerName, Util::$oauth2idps)) {
714
                    // Log in with an OAuth2 IdP
715
                    static::redirectToGetOAuth2User($providerId);
716
                } elseif (Util::getIdpList()->exists($providerId)) {
717
                    // Log in with InCommon
718
                    static::redirectToGetShibUser($providerId);
719
                } else { // $providerId not in whitelist
720
                    if (strlen($pn) > 0) {
721
                        $pc->set('providerId', '');
722
                        $pc->write();
723
                    } else {
724
                        Util::unsetCookieVar('providerId');
725
                    }
726
                    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

726
                    /** @scrutinizer ignore-call */ 
727
                    printLogonPage();
Loading history...
727
                }
728
            } else { // selected_idp does not match saved providerId
729
                if (strlen($pn) > 0) {
730
                    $pc->set('keepidp', '');
731
                    $pc->write();
732
                } else {
733
                    Util::unsetCookieVar('keepidp');
734
                }
735
                printLogonPage();
736
            }
737
        } else { // One of providerId or keepidp was not set
738
            printLogonPage();
739
        }
740
    }
741
742
    /**
743
     * printIcon
744
     *
745
     * This function prints out the HTML for the little icons which can
746
     * appear inline with other information.  This is accomplished via the
747
     * use of wrapping the image in a <span> tag.
748
     *
749
     * @param string $icon The prefix of the '...Icon.png' image to be
750
     *        shown. E.g., to show 'errorIcon.png', pass in 'error'.
751
     * @param string $popuptext (Optionals) The popup 'title' text to be
752
     *        displayed when the  mouse cursor hovers over the icon.
753
     *        Defaults to empty string.
754
     * @param string $class (Optionals) A CSS class for the icon. Will be
755
     *        appended after the 'helpcursor' class. Defaults to empty
756
     *        string.
757
     */
758
    public static function printIcon($icon, $popuptext = '', $class = '')
759
    {
760
        echo '<span';
761
        if (strlen($popuptext) > 0) {
762
            echo ' class="helpcursor ' , $class , '" title="' , $popuptext , '"';
763
        }
764
        echo '>&nbsp;<img src="/images/' , $icon , 'Icon.png"
765
              alt="&laquo; ' , ucfirst($icon) , '"
766
              width="14" height="14" /></span>';
767
    }
768
769
    /**
770
     * printHelpButton
771
     *
772
     * This function prints the 'Show Help' / 'Hide Help' button in the
773
     * upper-right corner of the main box area on the page.
774
     */
775
    public static function printHelpButton()
776
    {
777
        echo '
778
        <div class="helpbutton">
779
        ';
780
781
        static::printFormHead();
782
783
        echo '
784
          <input type="submit" name="submit" class="helpbutton" value="' ,
785
          (Util::getSessionVar('showhelp') == 'on' ? 'Hide' : 'Show') , '&#10; Help " />
786
          </form>
787
        </div>
788
        ';
789
    }
790
791
    /**
792
     * verifyCurrentUserSession
793
     *
794
     * This function verifies the contents of the PHP session.  It checks
795
     * the following:
796
     * (1) The persistent store 'uid', the Identity Provider 'idp', the
797
     *     IdP Display Name 'idpname', and the 'status' (of getUser()) are
798
     *     all non-empty strings.
799
     * (2) The 'status' (of getUser()) is even (i.e. STATUS_OK).
800
     * (3) If $providerId is passed-in, it must match 'idp'.
801
     * If all checks are good, then this function returns true.
802
     *
803
     * @param string $providerId (Optional) The user-selected Identity
804
     *        Provider. If set, make sure $providerId matches the PHP
805
     *        session variable 'idp'.
806
     * @return bool True if the contents of the PHP session ar valid.
807
     *              False otherwise.
808
     */
809
    public static function verifyCurrentUserSession($providerId = '')
810
    {
811
        $retval = false;
812
813
        // Check for eduGAIN IdP and possible get cert context
814
        if (Util::isEduGAINAndGetCert()) {
815
            Util::unsetUserSessionVars();
816
        }
817
818
        $idp       = Util::getSessionVar('idp');
819
        $idpname   = Util::getSessionVar('idpname');
820
        $uid       = Util::getSessionVar('uid');
821
        $status    = Util::getSessionVar('status');
822
        $dn        = Util::getSessionVar('dn');
823
        $authntime = Util::getSessionVar('authntime');
824
825
826
        if (
827
            (strlen($uid) > 0) && (strlen($idp) > 0) &&
828
            (strlen($idpname) > 0) && (strlen($status) > 0) &&
829
            (strlen($dn) > 0) && (strlen($authntime) > 0) &&
830
            (!($status & 1))
831
        ) {  // All STATUS_OK codes are even
832
            if ((strlen($providerId) == 0) || ($providerId == $idp)) {
833
                $retval = true;
834
            }
835
        }
836
837
        // As a final check, see if the IdP requires a forced skin
838
        if ($retval) {
839
            Util::getSkin()->init();
840
        }
841
842
        return $retval;
843
    }
844
845
    /**
846
     * redirectToGetShibUser
847
     *
848
     * This method redirects control flow to the getuser script for
849
     * If the first parameter (a whitelisted entityId) is not specified,
850
     * we check to see if either the providerId PHP session variable or the
851
     * providerId cookie is set (in that order) and use one if available.
852
     * The function then checks to see if there is a valid PHP session
853
     * and if the providerId matches the 'idp' in the session.  If so, then
854
     * we don't need to redirect to '/secure/getuser/' and instead we
855
     * we display the main page.  However, if the PHP session is not valid,
856
     * then this function redirects to the '/secure/getuser/' script so as
857
     * to do a Shibboleth authentication via mod_shib. When the providerId
858
     * is non-empty, the SessionInitiator will automatically go to that IdP
859
     * (i.e. without stopping at a WAYF).  This function also sets
860
     * several PHP session variables that are needed by the getuser script,
861
     * including the 'responsesubmit' variable which is set as the return
862
     * 'submit' variable in the 'getuser' script.
863
     *
864
     * @param string $providerId (Optional) An entityId of the
865
     *        authenticating IdP. If not specified (or set to the empty
866
     *        string), we check providerId PHP session variable and
867
     *        providerId cookie (in that order) for non-empty values.
868
     * @param string $responsesubmit (Optional) The value of the PHP session
869
     *       'submit' variable to be set upon return from the 'getuser'
870
     *        script.  This is utilized to control the flow of this script
871
     *        after 'getuser'. Defaults to 'gotuser'.
872
     * @param string responseurl (Optional) A response url for redirection
0 ignored issues
show
Bug introduced by
The type CILogon\Service\responseurl was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
873
     *        after successful processing at /secure/getuser/. Defaults to
874
     *        the current script directory.
875
     * @param bool $allowsilver Is it okay to request silver assurance in
876
     *        the authnContextClassRef? If not, then ignore the 'Request
877
     *        Silver' checkbox and silver certification in metadata.
878
     *        Defaults to true.
879
     */
880
    public static function redirectToGetShibUser(
881
        $providerId = '',
882
        $responsesubmit = 'gotuser',
883
        $responseurl = null,
884
        $allowsilver = true
885
    ) {
886
887
        // If providerId not set, try the cookie value
888
        if (strlen($providerId) == 0) {
889
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
890
        }
891
892
        // If the user has a valid 'uid' in the PHP session, and the
893
        // providerId matches the 'idp' in the PHP session, then
894
        // simply go to the main page.
895
        if (static::verifyCurrentUserSession($providerId)) {
896
            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

896
            /** @scrutinizer ignore-call */ 
897
            printMainPage();
Loading history...
897
        } else { // Otherwise, redirect to the getuser script
898
            // Set PHP session varilables needed by the getuser script
899
            Util::setSessionVar(
900
                'responseurl',
901
                (is_null($responseurl) ?
902
                    Util::getScriptDir(true) : $responseurl)
903
            );
904
            Util::setSessionVar('submit', 'getuser');
905
            Util::setSessionVar('responsesubmit', $responsesubmit);
906
            Util::getCsrf()->setCookieAndSession();
907
908
            // Set up the 'header' string for redirection thru mod_shib
909
            $mhn = static::getMachineHostname($providerId);
910
            $redirect = "Location: https://$mhn/Shibboleth.sso/Login?target=" .
911
                urlencode("https://$mhn/secure/getuser/");
912
913
            if (strlen($providerId) > 0) {
914
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
915
                if ($providerId == 'urn:mace:incommon:nih.gov') {
916
                    $redirect = preg_replace(
917
                        '%/Shibboleth.sso/Login%',
918
                        '/Shibboleth.sso/NIHLogin',
919
                        $redirect
920
                    );
921
                }
922
923
                $redirect .= '&providerId=' . urlencode($providerId);
924
925
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
926
                $forceauthn = Util::getSessionVar('forceauthn');
927
                Util::unsetSessionVar('forceauthn');
928
                if ($forceauthn) {
929
                    $redirect .= '&forceAuthn=true';
930
                } elseif (strlen($forceauthn) == 0) {
931
                    // 'forceauth' was not set to '0' in the session, so
932
                    // check the skin's option instead.
933
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
934
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
935
                        $redirect .= '&forceAuthn=true';
936
                    }
937
                }
938
939
                // If Silver IdP or 'Request Silver' checked, send extra parameter
940
                if ($allowsilver) {
941
                    if (
942
                        (Util::getIdpList()->isSilver($providerId)) ||
943
                        (strlen(Util::getPostVar('silveridp')) > 0)
944
                    ) {
945
                        Util::setSessionVar('requestsilver', '1');
946
                        $redirect .= '&authnContextClassRef=' .
947
                            urlencode('http://id.incommon.org/assurance/silver');
948
                    }
949
                }
950
            }
951
952
            $log = new Loggit();
953
            $log->info('Shibboleth Login="' . $redirect . '"');
954
            header($redirect);
955
            exit; // No further processing necessary
956
        }
957
    }
958
959
    /**
960
     * redirectToGetOAuth2User
961
     *
962
     * This method redirects control flow to the getuser script for
963
     * when the user logs in via OAuth 2.0. It first checks to see
964
     * if we have a valid session. If so, we don't need to redirect and
965
     * instead simply show the Get Certificate page. Otherwise, we start
966
     * an OAuth 2.0 logon by composing a parameterized GET URL using
967
     * the OAuth 2.0 endpoint.
968
     *
969
     * @param string $providerId (Optional) An entityId of the
970
     *        authenticating IdP. If not specified (or set to the empty
971
     *        string), we check providerId PHP session variable and
972
     *        providerId cookie (in that order) for non-empty values.
973
     * @param string $responsesubmit (Optional) The value of the PHP session
974
     *        'submit' variable to be set upon return from the 'getuser'
975
     *         script.  This is utilized to control the flow of this script
976
     *         after 'getuser'. Defaults to 'gotuser'.
977
     */
978
    public static function redirectToGetOAuth2User(
979
        $providerId = '',
980
        $responsesubmit = 'gotuser'
981
    ) {
982
        // If providerId not set, try the cookie value
983
        if (strlen($providerId) == 0) {
984
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
985
        }
986
987
        // If the user has a valid 'uid' in the PHP session, and the
988
        // providerId matches the 'idp' in the PHP session, then
989
        // simply go to the 'Download Certificate' button page.
990
        if (static::verifyCurrentUserSession($providerId)) {
991
            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

991
            /** @scrutinizer ignore-call */ 
992
            printMainPage();
Loading history...
992
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
993
            // Set PHP session varilables needed by the getuser script
994
            Util::unsetSessionVar('logonerror');
995
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
996
            Util::setSessionVar('submit', 'getuser');
997
            Util::setSessionVar('responsesubmit', $responsesubmit);
998
            $csrf = Util::getCsrf();
999
            $csrf->setCookieAndSession();
1000
            $extraparams = array();
1001
            $extraparams['state'] = $csrf->getTokenValue();
1002
1003
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
1004
            $forceauthn = Util::getSessionVar('forceauthn');
1005
            Util::unsetSessionVar('forceauthn');
1006
            if ($forceauthn) {
1007
                $extraparams['approval_prompt'] = 'force';
1008
            } elseif (strlen($forceauthn) == 0) {
1009
                // 'forceauth' was not set to '0' in the session, so
1010
                // check the skin's option instead.
1011
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
1012
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
1013
                    $extraparams['approval_prompt'] = 'force';
1014
                }
1015
            }
1016
1017
            // Get the provider name based on the provider authz URL
1018
            $providerName = Util::getAuthzIdP($providerId);
1019
1020
            // Get the authz URL and redirect
1021
            $oauth2 = new OAuth2Provider($providerName);
1022
            if (is_null($oauth2->provider)) {
1023
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
1024
                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

1024
                /** @scrutinizer ignore-call */ 
1025
                printLogonPage();
Loading history...
1025
            } else {
1026
                $authUrl = $oauth2->provider->getAuthorizationUrl(
1027
                    array_merge(
1028
                        $oauth2->authzUrlOpts,
1029
                        $extraparams
1030
                    )
1031
                );
1032
                header('Location: ' . $authUrl);
1033
                exit; // No further processing necessary
1034
            }
1035
        }
1036
    }
1037
1038
    /**
1039
     * printErrorBox
1040
     *
1041
     * This function prints out a bordered box with an error icon and any
1042
     * passed-in error HTML text.  The error icon and text are output to
1043
     * a <table> so as to keep the icon to the left of the error text.
1044
     *
1045
     * @param string $errortext HTML error text to be output
1046
     */
1047
    public static function printErrorBox($errortext)
1048
    {
1049
        echo '
1050
        <div class="errorbox">
1051
        <table cellpadding="5">
1052
        <tr>
1053
        <td valign="top">
1054
        ';
1055
        static::printIcon('error');
1056
        echo '&nbsp;
1057
        </td>
1058
        <td> ' , $errortext , '
1059
        </td>
1060
        </tr>
1061
        </table>
1062
        </div>
1063
        ';
1064
    }
1065
1066
    /**
1067
     * handleGotUser
1068
     *
1069
     * This function is called upon return from one of the getuser scripts
1070
     * which should have set the 'uid' and 'status' PHP session variables.
1071
     * It verifies that the status return is one of STATUS_OK (even
1072
     * values).  If not, we print an error message to the user.
1073
     */
1074
    public static function handleGotUser()
1075
    {
1076
        $log = new Loggit();
1077
        $uid = Util::getSessionVar('uid');
1078
        $status = Util::getSessionVar('status');
1079
1080
        // We must get and unset session vars BEFORE any HTML output since
1081
        // a redirect may go to another site, meaning we need to update
1082
        // the session cookie before we leave the cilogon.org domain.
1083
        $ePPN         = Util::getSessionVar('ePPN');
1084
        $ePTID        = Util::getSessionVar('ePTID');
1085
        $firstname    = Util::getSessionVar('firstname');
1086
        $lastname     = Util::getSessionVar('lastname');
1087
        $displayname  = Util::getSessionVar('displayname');
1088
        $emailaddr    = Util::getSessionVar('emailaddr');
1089
        $idp          = Util::getSessionVar('idp');
1090
        $idpname      = Util::getSessionVar('idpname');
1091
        $affiliation  = Util::getSessionVar('affiliation');
1092
        $ou           = Util::getSessionVar('ou');
1093
        $memberof     = Util::getSessionVar('memberof');
1094
        $acr          = Util::getSessionVar('acr');
1095
        $entitlement  = Util::getSessionVar('entitlement');
1096
        $itrustuin    = Util::getSessionVar('itrustuin');
1097
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1098
        $failureuri   = Util::getSessionVar('failureuri');
1099
1100
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
1101
        // If found, set 'Proceed' button redirect appropriately.
1102
        $redirect = '';
1103
        $redirectform = '';
1104
        // First, check for OIDC redirect_uri, with parameters in <form>
1105
        if (isset($clientparams['redirect_uri'])) {
1106
            $redirect = $clientparams['redirect_uri'];
1107
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
1108
                '<input type="hidden" name="error_description" value="Missing attributes" />';
1109
            if (isset($clientparams['state'])) {
1110
                $redirectform .= '<input type="hidden" name="state" value="' .
1111
                    $clientparams['state'] . '" />';
1112
            }
1113
        }
1114
        // Next, check for OAuth 1.0a
1115
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
1116
            $redirect = $failureuri . "?reason=missing_attributes";
1117
        }
1118
1119
        // If empty 'uid' or 'status' or odd-numbered status code, error!
1120
        if ((strlen($uid) == 0) || (strlen($status) == 0) || ($status & 1)) {
1121
            // Got all session vars by now, so okay to unset.
1122
            Util::unsetAllUserSessionVars();
1123
1124
            $log->error('Failed to getuser.');
1125
1126
            static::printHeader('Error Logging On');
1127
1128
            echo '
1129
            <div class="boxed">
1130
            ';
1131
1132
            if ($status == DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) {
1133
                // Check if the problem IdP was an OAuth2 IdP;
1134
                // probably no first/last name
1135
                if ($idpname == 'Google') {
1136
                    static::printErrorBox('
1137
                    <p>
1138
                    There was a problem logging on. It appears that you have
1139
                    attempted to use Google as your identity provider, but your
1140
                    name or email address was missing. To rectify this problem,
1141
                    go to the <a target="_blank"
1142
                    href="https://myaccount.google.com/privacy#personalinfo">Google
1143
                    Account Personal Information page</a>, and enter your first
1144
                    name, last name, and email address. (All other Google
1145
                    account information is not required by the CILogon Service.)
1146
                    </p>
1147
                    <p>
1148
                    After you have updated your Google account profile, click
1149
                    the "Proceed" button below and attempt to log on
1150
                    with your Google account again. If you have any questions,
1151
                    please contact us at the email address at the bottom of the
1152
                    page.</p>
1153
                    ');
1154
1155
                    echo '
1156
                    <div>
1157
                    ';
1158
                    static::printFormHead($redirect, 'get');
1159
                    echo '
1160
                    <p class="centered">
1161
                    <input type="hidden" name="providerId" value="' ,
1162
                    Util::getAuthzUrl('Google') , '" /> ' , $redirectform , '
1163
                    <input type="submit" name="submit" class="submit"
1164
                    value="Proceed" />
1165
                    </p>
1166
                    </form>
1167
                    </div>
1168
                    ';
1169
                } elseif ($idpname == 'GitHub') {
1170
                    static::printErrorBox('
1171
                    <p>
1172
                    There was a problem logging on. It appears that you have
1173
                    attempted to use GitHub as your identity provider, but your
1174
                    name or email address was missing. To rectify this problem,
1175
                    go to the <a target="_blank"
1176
                    href="https://github.com/settings/profile">GitHub
1177
                    Public Profile page</a>, and enter your name and email address.
1178
                    (All other GitHub account information is not required by
1179
                    the CILogon Service.)
1180
                    </p>
1181
                    <p>
1182
                    After you have updated your GitHub account profile, click
1183
                    the "Proceed" button below and attempt to log on
1184
                    with your GitHub account again. If you have any questions,
1185
                    please contact us at the email address at the bottom of the
1186
                    page.</p>
1187
                    ');
1188
1189
                    echo '
1190
                    <div>
1191
                    ';
1192
                    static::printFormHead($redirect, 'get');
1193
                    echo '
1194
                    <p class="centered">
1195
                    <input type="hidden" name="providerId" value="' ,
1196
                    Util::getAuthzUrl('GitHub') , '" /> ' , $redirectform , '
1197
                    <input type="submit" name="submit" class="submit"
1198
                    value="Proceed" />
1199
                    </p>
1200
                    </form>
1201
                    </div>
1202
                    ';
1203
                } elseif ($idpname == 'ORCID') {
1204
                    static::printErrorBox('
1205
                    <p>
1206
                    There was a problem logging on. It appears that you have
1207
                    attempted to use ORCID as your identity provider, but your
1208
                    name or email address was missing. To rectify this problem,
1209
                    go to your <a target="_blank"
1210
                    href="https://orcid.org/my-orcid">ORCID
1211
                    Profile page</a>, enter your name and email address, and
1212
                    make sure they can be viewed by Everyone.
1213
                    (All other ORCID account information is not required by
1214
                    the CILogon Service.)
1215
                    </p>
1216
                    <p>
1217
                    After you have updated your ORCID account profile, click
1218
                    the "Proceed" button below and attempt to log on
1219
                    with your ORCID account again. If you have any questions,
1220
                    please contact us at the email address at the bottom of the
1221
                    page.</p>
1222
                    ');
1223
1224
                    echo '
1225
                    <div>
1226
                    ';
1227
                    static::printFormHead($redirect, 'get');
1228
                    echo '
1229
                    <p class="centered">
1230
                    <input type="hidden" name="providerId" value="' ,
1231
                    Util::getAuthzUrl('ORCID') , '" /> ' , $redirectform , '
1232
                    <input type="submit" name="submit" class="submit"
1233
                    value="Proceed" />
1234
                    </p>
1235
                    </form>
1236
                    </div>
1237
                    ';
1238
                } else { // Problem was missing SAML attribute from Shib IdP
1239
                    static::printAttributeReleaseErrorMessage(
1240
                        $ePPN,
1241
                        $ePTID,
1242
                        $firstname,
1243
                        $lastname,
1244
                        $displayname,
1245
                        $emailaddr,
1246
                        $idp,
1247
                        $idpname,
1248
                        $affiliation,
1249
                        $ou,
1250
                        $memberof,
1251
                        $acr,
1252
                        $entitlement,
1253
                        $itrustuin,
1254
                        $clientparams,
1255
                        $redirect,
1256
                        $redirectform,
1257
                        Util::isEduGAINAndGetCert($idp, $idpname)
1258
                    );
1259
                }
1260
            } else {
1261
                static::printErrorBox('An internal error has occurred. System
1262
                    administrators have been notified. This may be a temporary
1263
                    error. Please try again later, or contact us at the the email
1264
                    address at the bottom of the page.');
1265
1266
                echo '
1267
                <div>
1268
                ';
1269
                static::printFormHead($redirect, 'get');
1270
                echo $redirectform , '
1271
                <input type="submit" name="submit" class="submit" value="Proceed" />
1272
                </form>
1273
                </div>
1274
                ';
1275
            }
1276
1277
            echo '
1278
            </div>
1279
            ';
1280
            static::printFooter();
1281
        } elseif (Util::isEduGAINAndGetCert($idp, $idpname)) {
1282
            // If eduGAIN IdP and session can get a cert, then error!
1283
            // Got all session vars by now, so okay to unset.
1284
            Util::unsetAllUserSessionVars();
1285
1286
            $log->error('Failed to getuser due to eduGAIN IdP restriction.');
1287
1288
            static::printHeader('Error Logging On');
1289
1290
            echo '
1291
            <div class="boxed">
1292
            ';
1293
            static::printAttributeReleaseErrorMessage(
1294
                $ePPN,
1295
                $ePTID,
1296
                $firstname,
1297
                $lastname,
1298
                $displayname,
1299
                $emailaddr,
1300
                $idp,
1301
                $idpname,
1302
                $affiliation,
1303
                $ou,
1304
                $memberof,
1305
                $acr,
1306
                $entitlement,
1307
                $itrustuin,
1308
                $clientparams,
1309
                $redirect,
1310
                $redirectform,
1311
                true
1312
            );
1313
1314
            echo '
1315
            </div>
1316
            ';
1317
            static::printFooter();
1318
        } else { // Got one of the STATUS_OK status codes
1319
            // Extra security check: Once the user has successfully authenticated
1320
            // with an IdP, verify that the chosen IdP was actually whitelisted.
1321
            // If not, then set error message and show Select an Identity Provider
1322
            // page again.
1323
            Util::getSkin()->init();  // Check for forced skin
1324
            $idps = static::getCompositeIdPList();
1325
            $providerId = Util::getSessionVar('idp');
1326
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
1327
                Util::setSessionVar(
1328
                    'logonerror',
1329
                    'Invalid IdP selected. Please try again.'
1330
                );
1331
                Util::sendErrorAlert(
1332
                    'Authentication attempt using non-whitelisted IdP',
1333
                    'A user successfully authenticated with an IdP, however, the
1334
selected IdP was not in the list of whitelisted IdPs as determined
1335
by the current skin. This might indicate the user attempted to
1336
circumvent the security check in "handleGotUser()" for valid
1337
IdPs for the skin.'
1338
                );
1339
                Util::unsetCookieVar('providerId');
1340
                Util::unsetAllUserSessionVars();
1341
                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

1341
                /** @scrutinizer ignore-call */ 
1342
                printLogonPage();
Loading history...
1342
            }
1343
        }
1344
    }
1345
1346
    /**
1347
     * gotUserSuccess
1348
     *
1349
     * This function is called after the user has been successfully
1350
     * authenticated. If the 'status' session variable is STATUS_OK
1351
     * then it checks if we have a new or changed user and logs
1352
     * that appropriately. It then continues to the MainPage.
1353
     */
1354
    public static function gotUserSuccess()
1355
    {
1356
        $log = new Loggit();
1357
        $status = Util::getSessionVar('status');
1358
1359
        // If this is the first time the user has used the CILogon Service,
1360
        // and the flow is OAuth-based, send an alert if the name contains
1361
        // any HTML entities.
1362
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1363
        $callbackuri = Util::getSessionVar('callbackuri');
1364
1365
        if (
1366
            ($status == DBService::$STATUS['STATUS_NEW_USER']) &&
1367
            ((strlen($callbackuri) > 0) ||
1368
             (isset($clientparams['code'])))
1369
        ) {
1370
            // Extra check for new users: see if any HTML entities
1371
            // are in the user name. If so, send an email alert.
1372
            $dn = Util::getSessionVar('dn');
1373
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
1374
            $htmldn = Util::htmlent($dn);
1375
            if (strcmp($dn, $htmldn) != 0) {
1376
                Util::sendErrorAlert(
1377
                    'New user DN contains HTML entities',
1378
                    "htmlentites(DN) = $htmldn\n"
1379
                );
1380
            }
1381
        }
1382
1383
        // For a new user, or if the user got new attributes, just log it.
1384
        // Then proceed to the Main Page.
1385
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
1386
            $log->info('New User.');
1387
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
1388
            $log->info('User IdP attributes changed.');
1389
        }
1390
        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

1390
        /** @scrutinizer ignore-call */ 
1391
        printMainPage();
Loading history...
1391
    }
1392
1393
    /**
1394
     * generateP12
1395
     *
1396
     * This function is called when the user clicks the 'Get New
1397
     * Certificate' button. It first reads in the password fields and
1398
     * verifies that they are valid (i.e. they are long enough and match).
1399
     * Then it gets a credential from the MyProxy server and converts that
1400
     * certificate into a PKCS12 which is written to disk.  If everything
1401
     * succeeds, the temporary pkcs12 directory and lifetime is saved to
1402
     * the 'p12' PHP session variable, which is read later when the Main
1403
     * Page HTML is shown.
1404
     */
1405
    public static function generateP12()
1406
    {
1407
        $log = new Loggit();
1408
1409
        // Get the entered p12lifetime and p12multiplier and set the cookies
1410
        list($minlifetime, $maxlifetime) =
1411
            static::getMinMaxLifetimes('pkcs12', 9516);
1412
        $p12lifetime   = Util::getPostVar('p12lifetime');
1413
        $p12multiplier = Util::getPostVar('p12multiplier');
1414
        if (strlen($p12multiplier) == 0) {
1415
            $p12multiplier = 1;  // For ECP, p12lifetime is in hours
1416
        }
1417
        $lifetime = $p12lifetime * $p12multiplier;
1418
        if ($lifetime <= 0) { // In case user entered negative number
1419
            $lifetime = $maxlifetime;
1420
            $p12lifetime = $maxlifetime;
1421
            $p12multiplier = 1;  // maxlifetime is in hours
1422
        } elseif ($lifetime < $minlifetime) {
1423
            $lifetime = $minlifetime;
1424
            $p12lifetime = $minlifetime;
1425
            $p12multiplier = 1;  // minlifetime is in hours
1426
        } elseif ($lifetime > $maxlifetime) {
1427
            $lifetime = $maxlifetime;
1428
            $p12lifetime = $maxlifetime;
1429
            $p12multiplier = 1;  // maxlifetime is in hours
1430
        }
1431
        Util::setCookieVar('p12lifetime', $p12lifetime);
1432
        Util::setCookieVar('p12multiplier', $p12multiplier);
1433
        Util::setSessionVar('p12lifetime', $p12lifetime);
1434
        Util::setSessionVar('p12multiplier', $p12multiplier);
1435
1436
        // Verify that the password is at least 12 characters long
1437
        $password1 = Util::getPostVar('password1');
1438
        $password2 = Util::getPostVar('password2');
1439
        $p12password = Util::getPostVar('p12password');  // For ECP clients
1440
        if (strlen($p12password) > 0) {
1441
            $password1 = $p12password;
1442
            $password2 = $p12password;
1443
        }
1444
        if (strlen($password1) < 12) {
1445
            Util::setSessionVar(
1446
                'p12error',
1447
                'Password must have at least 12 characters.'
1448
            );
1449
            return; // SHORT PASSWORD - NO FURTHER PROCESSING NEEDED!
1450
        }
1451
1452
        // Verify that the two password entry fields matched
1453
        if ($password1 != $password2) {
1454
            Util::setSessionVar('p12error', 'Passwords did not match.');
1455
            return; // MISMATCHED PASSWORDS - NO FURTHER PROCESSING NEEDED!
1456
        }
1457
1458
        // Set the port based on the Level of Assurance
1459
        $port = 7512;
1460
        $loa = Util::getSessionVar('loa');
1461
        if ($loa == 'http://incommonfederation.org/assurance/silver') {
1462
            $port = 7514;
1463
        } elseif ($loa == 'openid') {
1464
            $port = 7516;
1465
        }
1466
1467
        $dn = Util::getSessionVar('dn');
1468
        if (strlen($dn) > 0) {
1469
            // Append extra info, such as 'skin', to be processed by MyProxy
1470
            $myproxyinfo = Util::getSessionVar('myproxyinfo');
1471
            if (strlen($myproxyinfo) > 0) {
1472
                $dn .= " $myproxyinfo";
1473
            }
1474
            // Attempt to fetch a credential from the MyProxy server
1475
            $cert = MyProxy::getMyProxyCredential(
1476
                $dn,
1477
                '',
1478
                'myproxy.cilogon.org,myproxy2.cilogon.org',
1479
                $port,
1480
                $lifetime,
1481
                '/var/www/config/hostcred.pem',
1482
                ''
1483
            );
1484
1485
            // The 'openssl pkcs12' command is picky in that the private
1486
            // key must appear BEFORE the public certificate. But MyProxy
1487
            // returns the private key AFTER. So swap them around.
1488
            $cert2 = '';
1489
            if (
1490
                preg_match(
1491
                    '/-----BEGIN CERTIFICATE-----([^-]+)' .
1492
                    '-----END CERTIFICATE-----[^-]*' .
1493
                    '-----BEGIN RSA PRIVATE KEY-----([^-]+)' .
1494
                    '-----END RSA PRIVATE KEY-----/',
1495
                    $cert,
1496
                    $match
1497
                )
1498
            ) {
1499
                $cert2 = "-----BEGIN RSA PRIVATE KEY-----" .
1500
                         $match[2] . "-----END RSA PRIVATE KEY-----\n" .
1501
                         "-----BEGIN CERTIFICATE-----" .
1502
                         $match[1] . "-----END CERTIFICATE-----";
1503
            }
1504
1505
            if (strlen($cert2) > 0) { // Successfully got a certificate!
1506
                // Create a temporary directory in /var/www/html/pkcs12/
1507
                $tdirparent = '/var/www/html/pkcs12/';
1508
                $polonum = '3';   // Prepend the polo? number to directory
1509
                if (preg_match('/(\d+)\./', php_uname('n'), $polomatch)) {
1510
                    $polonum = $polomatch[1];
1511
                }
1512
                $tdir = Util::tempDir($tdirparent, $polonum, 0770);
1513
                $p12dir = str_replace($tdirparent, '', $tdir);
1514
                $p12file = $tdir . '/usercred.p12';
1515
1516
                // Call the openssl pkcs12 program to convert certificate
1517
                exec('/bin/env ' .
1518
                     'RANDFILE=/tmp/.rnd ' .
1519
                     'CILOGON_PKCS12_PW=' . escapeshellarg($password1) . ' ' .
1520
                     '/usr/bin/openssl pkcs12 -export ' .
1521
                     '-passout env:CILOGON_PKCS12_PW ' .
1522
                     "-out $p12file " .
1523
                     '<<< ' . escapeshellarg($cert2));
1524
1525
                // Verify the usercred.p12 file was actually created
1526
                $size = @filesize($p12file);
1527
                if (($size !== false) && ($size > 0)) {
1528
                    $p12link = 'https://' . static::getMachineHostname() .
1529
                               '/pkcs12/' . $p12dir . '/usercred.p12';
1530
                    $p12 = (time() + 300) . " " . $p12link;
1531
                    Util::setSessionVar('p12', $p12);
1532
                    $log->info('Generated New User Certificate="' . $p12link . '"');
1533
                    //CIL-507 Special Log Message For XSEDE
1534
                    $log->info('USAGE email="' .
1535
                        Util::getSessionVar('emailaddr') . '" client="PKCS12"');
1536
                } else { // Empty or missing usercred.p12 file - shouldn't happen!
1537
                    Util::setSessionVar(
1538
                        'p12error',
1539
                        'Error creating certificate. Please try again.'
1540
                    );
1541
                    Util::deleteDir($tdir); // Remove the temporary directory
1542
                    $log->info('Error creating certificate - missing usercred.p12');
1543
                }
1544
            } else { // The myproxy-logon command failed - shouldn't happen!
1545
                Util::setSessionVar(
1546
                    'p12error',
1547
                    'Error! MyProxy unable to create certificate.'
1548
                );
1549
                $log->info('Error creating certificate - myproxy-logon failed');
1550
            }
1551
        } else { // Couldn't find the 'dn' PHP session value - shouldn't happen!
1552
            Util::setSessionVar(
1553
                'p12error',
1554
                'Missing username. Please enable cookies.'
1555
            );
1556
            $log->info('Error creating certificate - missing dn session variable');
1557
        }
1558
    }
1559
1560
    /**
1561
     * getLogOnButtonText
1562
     *
1563
     * This function checks the current skin to see if <logonbuttontext>
1564
     * has been configured.  If so, it returns that value.  Otherwise,
1565
     * it returns 'Log On'.
1566
     *
1567
     * @return string The text of the 'Log On' button for the WAYF, as
1568
     *         configured for the skin.  Defaults to 'Log On'.
1569
     */
1570
    public static function getLogOnButtonText()
1571
    {
1572
        $retval = 'Log On';
1573
        $lobt = Util::getSkin()->getConfigOption('logonbuttontext');
1574
        if (!is_null($lobt)) {
1575
            $retval = (string)$lobt;
1576
        }
1577
        return $retval;
1578
    }
1579
1580
    /**
1581
     * getSerialStringFromDN
1582
     *
1583
     * This function takes in a CILogon subject DN and returns just the
1584
     * serial string part (e.g., A325). This function is needed since the
1585
     * serial_string is not stored in the PHP session as a separate
1586
     * variable since it is always available in the 'dn' session variable.
1587
     *
1588
     * @param string $dn The certificate subject DN (typically found in the
1589
     *        session 'dn' variable)
1590
     * @return string The serial string extracted from the subject DN, or
1591
     *         empty string if DN is empty or wrong format.
1592
     */
1593
    public static function getSerialStringFromDN($dn)
1594
    {
1595
        $serial = ''; // Return empty string upon error
1596
1597
        // Strip off the email address, if present
1598
        $dn = preg_replace('/\s+email=.+$/', '', $dn);
1599
        // Find the 'CN=' entry
1600
        if (preg_match('%/DC=org/DC=cilogon/C=US/O=.*/CN=(.*)%', $dn, $match)) {
1601
            $cn = $match[1];
1602
            if (preg_match('/\s+([^\s]+)$/', $cn, $match)) {
1603
                $serial = $match[1];
1604
            }
1605
        }
1606
        return $serial;
1607
    }
1608
1609
    /**
1610
     * getEmailFromDN
1611
     *
1612
     * This function takes in a CILogon subject DN and returns just the
1613
     * email address part. This function is needed since the email address
1614
     * is not stored in the PHP session as a separate variable since it is
1615
     * always available in the 'dn' session variable.
1616
     *
1617
     * @param string $dn The certificate subject DN (typically found in the
1618
     *        session 'dn' variable)
1619
     * @return string The email address extracted from the subject DN, or
1620
     *         empty string if DN is empty or wrong format.
1621
     */
1622
    public static function getEmailFromDN($dn)
1623
    {
1624
        $email = ''; // Return empty string upon error
1625
        if (preg_match('/\s+email=(.+)$/', $dn, $match)) {
1626
            $email = $match[1];
1627
        }
1628
        return $email;
1629
    }
1630
1631
    /**
1632
     * reformatDN
1633
     *
1634
     * This function takes in a certificate subject DN with the email=...
1635
     * part already removed. It checks the skin to see if <dnformat> has
1636
     * been set. If so, it reformats the DN appropriately.
1637
     *
1638
     * @param string $dn The certificate subject DN (without the email=... part)
1639
     * @return string The certificate subject DN transformed according to
1640
     *         the value of the <dnformat> skin config option.
1641
     */
1642
    public static function reformatDN($dn)
1643
    {
1644
        $newdn = $dn;
1645
        $dnformat = (string)Util::getSkin()->getConfigOption('dnformat');
1646
        if (!is_null($dnformat)) {
0 ignored issues
show
introduced by
The condition is_null($dnformat) is always false.
Loading history...
1647
            if (
1648
                ($dnformat == 'rfc2253') &&
1649
                (preg_match(
1650
                    '%/DC=(.*)/DC=(.*)/C=(.*)/O=(.*)/CN=(.*)%',
1651
                    $dn,
1652
                    $match
1653
                ))
1654
            ) {
1655
                array_shift($match);
1656
                $m = array_reverse(Net_LDAP2_Util::escape_dn_value($match));
1657
                $newdn = "CN=$m[0],O=$m[1],C=$m[2],DC=$m[3],DC=$m[4]";
1658
            }
1659
        }
1660
        return $newdn;
1661
    }
1662
1663
    /**
1664
     * getMinMaxLifetimes
1665
     *
1666
     * This function checks the skin's configuration to see if either or
1667
     * both of minlifetime and maxlifetime in the specified config.xml
1668
     * block have been set. If not, default to minlifetime of 1 (hour) and
1669
     * the specified defaultmaxlifetime.
1670
     *
1671
     * @param string $section The XML section block from which to read the
1672
     *        minlifetime and maxlifetime values. Can be one of the
1673
     *        following: 'pkcs12' or 'delegate'.
1674
     * @param int $defaultmaxlifetime Default maxlifetime (in hours) for the
1675
     *        credential.
1676
     * @return array An array consisting of two entries: the minimum and
1677
     *         maximum lifetimes (in hours) for a credential.
1678
     */
1679
    public static function getMinMaxLifetimes($section, $defaultmaxlifetime)
1680
    {
1681
        $minlifetime = 1;    // Default minimum lifetime is 1 hour
1682
        $maxlifetime = $defaultmaxlifetime;
1683
        $skin = Util::getSkin();
1684
        $skinminlifetime = $skin->getConfigOption($section, 'minlifetime');
1685
        // Read the skin's minlifetime value from the specified section
1686
        if ((!is_null($skinminlifetime)) && ((int)$skinminlifetime > 0)) {
1687
            $minlifetime = max($minlifetime, (int)$skinminlifetime);
1688
            // Make sure $minlifetime is less than $maxlifetime;
1689
            $minlifetime = min($minlifetime, $maxlifetime);
1690
        }
1691
        // Read the skin's maxlifetime value from the specified section
1692
        $skinmaxlifetime = $skin->getConfigOption($section, 'maxlifetime');
1693
        if ((!is_null($skinmaxlifetime)) && ((int)$skinmaxlifetime) > 0) {
1694
            $maxlifetime = min($maxlifetime, (int)$skinmaxlifetime);
1695
            // Make sure $maxlifetime is greater than $minlifetime
1696
            $maxlifetime = max($minlifetime, $maxlifetime);
1697
        }
1698
1699
        return array($minlifetime, $maxlifetime);
1700
    }
1701
1702
    /**
1703
     * getMachineHostname
1704
     *
1705
     * This function is utilized in the formation of the URL for the
1706
     * PKCS12 credential download link.  It returns a host-specific
1707
     * URL hostname by mapping the local machine hostname (as returned
1708
     * by 'uname -n') to an InCommon metadata cilogon.org hostname
1709
     * (e.g., polo2.cilogon.org). This function contains an array
1710
     * '$hostnames' where the values are the local machine hostname and
1711
     * the keys are the *.cilogon.org hostname. Since this array is
1712
     * fairly static, I didn't see the need to read it in from a config
1713
     * file. In case the local machine hostname cannot be found in the
1714
     * $hostnames array, 'cilogon.org' is returned by default.
1715
     *
1716
     * @param string $idp The entityID of the IdP used for potential
1717
     *        special handling (e.g., for Syngenta).
1718
     * @return string The full cilogon-specific hostname of this host.
1719
     */
1720
    public static function getMachineHostname($idp = '')
1721
    {
1722
        $retval = 'cilogon.org';
1723
        // CIL-439 For Syngenta, use just a single 'hostname' value to
1724
        // match their Active Directory configuration for CILogon's
1725
        // assertionConsumerService URL.
1726
        if ($idp == 'https://sts.windows.net/06219a4a-a835-44d5-afaf-3926343bfb89/') {
1727
            $retval = 'cilogon.org'; // Set to cilogon.org for production
1728
        // Otherwise, map the local hostname to a *.cilogon.org domain name.
1729
        } else {
1730
            $hostnames = array(
1731
                "polo1.ncsa.illinois.edu"        => "polo1.cilogon.org" ,
1732
                "poloa.ncsa.illinois.edu"        => "polo1.cilogon.org" ,
1733
                "polo2.ncsa.illinois.edu"        => "polo2.cilogon.org" ,
1734
                "polob.ncsa.illinois.edu"        => "polo2.cilogon.org" ,
1735
                "fozzie.nics.utk.edu"            => "polo3.cilogon.org" ,
1736
                "poloc.ncsa.illinois.edu"        => "test.cilogon.org" ,
1737
                "polot.ncsa.illinois.edu"        => "test.cilogon.org" ,
1738
                "polo-staging.ncsa.illinois.edu" => "test.cilogon.org" ,
1739
                "polod.ncsa.illinois.edu"        => "dev.cilogon.org" ,
1740
            );
1741
            $localhost = php_uname('n');
1742
            if (array_key_exists($localhost, $hostnames)) {
1743
                $retval = $hostnames[$localhost];
1744
            }
1745
        }
1746
        return $retval;
1747
    }
1748
1749
    /**
1750
     * getCompositeIdPList
1751
     *
1752
     * This function generates a list of IdPs to display in the 'Select
1753
     * An Identity Provider' box on the main CILogon page or on the
1754
     * TestIdP page. For the main CILogon page, this is a filtered list of
1755
     * IdPs based on the skin's whitelist/blacklist and the global
1756
     * blacklist file. For the TestIdP page, the list is all InCommon IdPs.
1757
     *
1758
     * @param bool $samlidps (Optional) Show all SAML-based IdPs in
1759
     *        selection list? Defaults to false, which means show only
1760
     *        whitelisted IdPs.
1761
     * @return array A two-dimensional array where the primary key is the
1762
     *         entityID and the secondary key is either 'Display_Name'
1763
     *         or 'Organization_Name'.
1764
     */
1765
    public static function getCompositeIdPList($samlidps = false)
1766
    {
1767
        $retarray = array();
1768
1769
        $idplist = Util::getIdpList();
1770
        if ($samlidps) { // Get all SAML-based IdPs only
1771
            $retarray = $idplist->getSAMLIdPs();
1772
        } else { // Get the selected InCommon IdPs, plus maybe OAuth2 IdPs
1773
            $skin = Util::getSkin();
1774
1775
            // Check if the skin's config.xml has set the
1776
            // 'registeredbyincommonidps' option, which restricts the SAML-
1777
            // based IdPs to those with the <Registered_By_InCommon> tag.
1778
            // Otherwise, just get the SAML-based IdPs that have the
1779
            // <Whitelisted> tag. Note that the skin's <idpwhitelist>
1780
            // is still consulted in either case (below).
1781
            $registeredbyincommonidps = $skin->getConfigOption('registeredbyincommonidps');
1782
            if (
1783
                (!is_null($registeredbyincommonidps)) &&
1784
                ((int)$registeredbyincommonidps == 1)
1785
            ) {
1786
                $retarray = $idplist->getRegisteredByInCommonIdPs();
1787
            } else {
1788
                $retarray = $idplist->getWhitelistedIdPs();
1789
            }
1790
1791
            // Add all OAuth2 IdPs to the list
1792
            foreach (Util::$oauth2idps as $key => $value) {
1793
                $retarray[Util::getAuthzUrl($value)]['Organization_Name'] = $value;
1794
                $retarray[Util::getAuthzUrl($value)]['Display_Name'] = $value;
1795
            }
1796
1797
            // Check to see if the skin's config.xml has a whitelist of IDPs.
1798
            // If so, go thru master IdP list and keep only those IdPs in the
1799
            // config.xml's whitelist.
1800
            if ($skin->hasIdpWhitelist()) {
1801
                foreach ($retarray as $entityId => $names) {
1802
                    if (!$skin->idpWhitelisted($entityId)) {
1803
                        unset($retarray[$entityId]);
1804
                    }
1805
                }
1806
            }
1807
            // Next, check to see if the skin's config.xml has a blacklist of
1808
            // IdPs. If so, cull down the master IdP list removing 'bad' IdPs.
1809
            if ($skin->hasIdpBlacklist()) {
1810
                $idpblacklist = $skin->getConfigOption('idpblacklist');
1811
                foreach ($idpblacklist->idp as $blackidp) {
1812
                    unset($retarray[(string)$blackidp]);
1813
                }
1814
            }
1815
        }
1816
1817
        // Fix for CIL-174 - As suggested by Keith Hazelton, replace commas and
1818
        // hyphens with just commas.
1819
        $regex = '/(University of California)\s*[,-]\s*/';
1820
        foreach ($retarray as $entityId => $names) {
1821
            if (preg_match($regex, $names['Organization_Name'])) {
1822
                $retarray[$entityId]['Organization_Name'] =
1823
                    preg_replace($regex, '$1, ', $names['Organization_Name']);
1824
            }
1825
            if (preg_match($regex, $names['Display_Name'])) {
1826
                $retarray[$entityId]['Display_Name'] =
1827
                    preg_replace($regex, '$1, ', $names['Display_Name']);
1828
            }
1829
        }
1830
1831
        // Re-sort the retarray by Display_Name for correct alphabetization.
1832
        uasort($retarray, function ($a, $b) {
1833
            return strcasecmp(
1834
                $a['Display_Name'],
1835
                $b['Display_Name']
1836
            );
1837
        });
1838
1839
        return $retarray;
1840
    }
1841
1842
    /**
1843
     * printAttributeReleaseErrorMessage
1844
     *
1845
     * This is a convenience method called by handleGotUser to print out
1846
     * the attribute release error page to the user.
1847
     *
1848
     * @param string $ePPN
1849
     * @param string $ePTID
1850
     * @param string $firstname
1851
     * @param string $lastname
1852
     * @param string $displayname
1853
     * @param string $emailaddr
1854
     * @param string $idp
1855
     * @param string $idpname
1856
     * @param string $affiliation
1857
     * @param string $ou
1858
     * @param string $memberof
1859
     * @param string $acr
1860
     * @param string $entitlement
1861
     * @param string $itrustuin
1862
     * @param string $clientparams
1863
     * @param string $redirect
1864
     * @param string $redirectform
1865
     * @param bool   $edugainandgetcert
1866
     */
1867
    public static function printAttributeReleaseErrorMessage(
1868
        $ePPN,
1869
        $ePTID,
1870
        $firstname,
1871
        $lastname,
1872
        $displayname,
1873
        $emailaddr,
1874
        $idp,
1875
        $idpname,
1876
        $affiliation,
1877
        $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

1877
        /** @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...
1878
        $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

1878
        /** @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...
1879
        $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

1879
        /** @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...
1880
        $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

1880
        /** @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...
1881
        $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

1881
        /** @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...
1882
        $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

1882
        /** @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...
1883
        $redirect,
1884
        $redirectform,
1885
        $edugainandgetcert
1886
    ) {
1887
        $errorboxstr =
1888
        '<p>There was a problem logging on. Your identity
1889
        provider has not provided CILogon with required information.</p>
1890
        <blockquote><table cellpadding="5">';
1891
1892
        $missingattrs = '';
1893
        // Show user which attributes are missing
1894
        if ((strlen($ePPN) == 0) && (strlen($ePTID) == 0)) {
1895
            $errorboxstr .=
1896
            '<tr><th>ePTID:</th><td>MISSING</td></tr>
1897
            <tr><th>ePPN:</th><td>MISSING</td></tr>';
1898
            $missingattrs .= '%0D%0A    eduPersonPrincipalName' .
1899
                             '%0D%0A    eduPersonTargetedID ';
1900
        }
1901
        if ((strlen($firstname) == 0) && (strlen($displayname) == 0)) {
1902
            $errorboxstr .=
1903
            '<tr><th>First Name:</th><td>MISSING</td></tr>';
1904
            $missingattrs .= '%0D%0A    givenName (first name)';
1905
        }
1906
        if ((strlen($lastname) == 0) && (strlen($displayname) == 0)) {
1907
            $errorboxstr .=
1908
            '<tr><th>Last Name:</th><td>MISSING</td></tr>';
1909
            $missingattrs .= '%0D%0A    sn (last name)';
1910
        }
1911
        if (
1912
            (strlen($displayname) == 0) &&
1913
            ((strlen($firstname) == 0) || (strlen($lastname) == 0))
1914
        ) {
1915
            $errorboxstr .=
1916
            '<tr><th>Display Name:</th><td>MISSING</td></tr>';
1917
            $missingattrs .= '%0D%0A    displayName';
1918
        }
1919
        $emailvalid = filter_var($emailaddr, FILTER_VALIDATE_EMAIL);
1920
        if ((strlen($emailaddr) == 0) || (!$emailvalid)) {
1921
            $errorboxstr .=
1922
            '<tr><th>Email Address:</th><td>' .
1923
            ((strlen($emailaddr) == 0) ? 'MISSING' : 'INVALID') .
1924
            '</td></tr>';
1925
            $missingattrs .= '%0D%0A    mail (email address)';
1926
        }
1927
        // CIL-326/CIL-539 - For eduGAIN IdPs attempting to get a cert,
1928
        // print out missing R&S and SIRTFI values
1929
        $idplist = Util::getIdpList();
1930
        if ($edugainandgetcert) {
1931
            if (!$idplist->isREFEDSRandS($idp)) {
1932
                $errorboxstr .=
1933
                '<tr><th><a target="_blank"
1934
                href="http://refeds.org/category/research-and-scholarship">Research
1935
                and Scholarship</a>:</th><td>MISSING</td></tr>';
1936
                $missingattrs .= '%0D%0A    http://refeds.org/category/research-and-scholarship';
1937
            }
1938
            if (!$idplist->isSIRTFI($idp)) {
1939
                $errorboxstr .=
1940
                '<tr><th><a target="_blank"
1941
                href="https://refeds.org/sirtfi">SIRTFI</a>:</th><td>MISSING</td></tr>';
1942
                $missingattrs .= '%0D%0A    http://refeds.org/sirtfi';
1943
            }
1944
        }
1945
        $student = false;
1946
        $errorboxstr .= '</table></blockquote>';
1947
        if (
1948
            (strlen($emailaddr) == 0) &&
1949
            (preg_match('/student@/', $affiliation))
1950
        ) {
1951
            $student = true;
1952
            $errorboxstr .= '<p><b>If you are a student</b>, ' .
1953
            'you may need to ask your identity provider ' .
1954
            'to release your email address.</p>';
1955
        }
1956
1957
        // Get contacts from metadata for email addresses
1958
        $shibarray = $idplist->getShibInfo($idp);
1959
        $emailmsg = '?subject=Attribute Release Problem for CILogon' .
1960
        '&[email protected]' .
1961
        '&body=Hello, I am having trouble logging on to ' .
1962
        'https://cilogon.org/ using the ' . $idpname .
1963
        ' Identity Provider (IdP) ' .
1964
        'due to the following missing attributes:%0D%0A' .
1965
        $missingattrs;
1966
        if ($student) {
1967
            $emailmsg .= '%0D%0A%0D%0ANote that my account is ' .
1968
            'marked "student" and thus my email address may need ' .
1969
            'to be released.';
1970
        }
1971
        $emailmsg .= '%0D%0A%0D%0APlease see ' .
1972
            'http://www.cilogon.org/service/addidp for more ' .
1973
            'details. Thank you for any help you can provide.';
1974
        $errorboxstr .= '<p>Contact your identity provider to ' .
1975
        'let them know you are having having a problem logging on ' .
1976
        'to CILogon.</p><blockquote><ul>';
1977
1978
        $addrfound = false;
1979
        $name = @$shibarray['Support Name'];
1980
        $addr = @$shibarray['Support Address'];
1981
        $addr = preg_replace('/^mailto:/', '', $addr);
1982
1983
        if (strlen($addr) > 0) {
1984
            $addrfound = true;
1985
            if (strlen($name) == 0) { // Use address if no name given
1986
                $name = $addr;
1987
            }
1988
            $errorboxstr .= '<li> Support Contact: ' .
1989
                $name . ' &lt;<a href="mailto:' .
1990
                $addr . $emailmsg . '">' .
1991
                $addr . '</a>&gt;</li>';
1992
        }
1993
1994
        if (!$addrfound) {
1995
            $name = @$shibarray['Technical Name'];
1996
            $addr = @$shibarray['Technical Address'];
1997
            $addr = preg_replace('/^mailto:/', '', $addr);
1998
            if (strlen($addr) > 0) {
1999
                $addrfound = true;
2000
                if (strlen($name) == 0) { // Use address if no name given
2001
                    $name = $addr;
2002
                }
2003
                $errorboxstr .= '<li> Technical Contact: ' .
2004
                    $name . ' &lt;<a href="mailto:' .
2005
                    $addr . $emailmsg . '">' .
2006
                    $addr . '</a>&gt;</li>';
2007
            }
2008
        }
2009
2010
        if (!$addrfound) {
2011
            $name = @$shibarray['Administrative Name'];
2012
            $addr = @$shibarray['Administrative Address'];
2013
            $addr = preg_replace('/^mailto:/', '', $addr);
2014
            if (strlen($addr) > 0) {
2015
                if (strlen($name) == 0) { // Use address if no name given
2016
                    $name = $addr;
2017
                }
2018
                $errorboxstr .= '<li>Administrative Contact: ' .
2019
                    $name . ' &lt;<a href="mailto:' .
2020
                    $addr . $emailmsg . '">' .
2021
                    $addr . '</a>&gt</li>';
2022
            }
2023
        }
2024
2025
        $errorboxstr .= '</ul></blockquote>
2026
2027
        <p> Alternatively, you can contact us at the email address
2028
        at the bottom of the page.</p>
2029
        ';
2030
2031
        static::printErrorBox($errorboxstr);
2032
2033
        echo '
2034
        <div>
2035
        ';
2036
2037
        static::printFormHead($redirect, 'get');
2038
        echo $redirectform , '
2039
        <input type="submit" name="submit" class="submit"
2040
        value="Proceed" />
2041
        </form>
2042
        </div>
2043
        ';
2044
    }
2045
2046
    /**
2047
     * getIdphintList
2048
     *
2049
     * This function adds support for AARC-G049 "IdP Hinting". It
2050
     * searches both the GET query parameters and the OIDC client
2051
     * parameters passed to the 'authorize' endpoint for a parameter
2052
     * named either 'selected_idp' or 'idphint'. This parameter can be
2053
     * a single entityId or a comma-separated list of entityIds.
2054
     * The entries in the list are processed to remove any 'chained'
2055
     * idphints and also to transform OIDC 'issuer' values into
2056
     * CILogon-specific 'entityIds' as used in the 'Select an IdP'
2057
     * list. Any idps which are not in the current skin's 'Select
2058
     * an IdP' list are removed. The resulting processed list of
2059
     * entityIds is returned, which may be an empty array.
2060
     *
2061
     * @param array $idps (Optional) A list of valid (i.e., whitelisted) IdPs.
2062
     *        If this list is empty, then use the current skin's IdP list.
2063
     * @return array A list of entityIds / OIDC provider URLs extracted from
2064
     *         a passed-in parameter 'selected_idp' or 'idphint'. This array
2065
     *         may be empty if no such parameter was found, or if the
2066
     *         entityIds in the list were not valid.
2067
     */
2068
    public static function getIdphintList($idps = [])
2069
    {
2070
        // Check for either 'selected_idp' or 'idphint' parameter that was
2071
        // passed in via a query parameter, either for an OAuth transaction
2072
        // or just 'normally'. Note that if both 'selected_idp' and
2073
        // 'idphint' were passed, 'idphint' takes priority.
2074
2075
        $hintarray = array();
2076
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2077
2078
        $hintstr = '';
2079
        if (!empty(@$clientparams['idphint'])) {
2080
            $hintstr = $clientparams['idphint'];
2081
        } elseif (!empty(Util::getGetVar('idphint'))) {
2082
            $hintstr = Util::getGetVar('idphint');
2083
        } elseif (!empty(@$clientparams['selected_idp'])) {
2084
            $hintstr = $clientparams['selected_idp'];
2085
        } elseif (!empty(Util::getGetVar('selected_idp'))) {
2086
            $hintstr = Util::getGetVar('selected_idp');
2087
        }
2088
2089
        if (!empty($hintstr)) {
2090
            // Split on comma to account for multiple idps
2091
            $hintarray = explode(',', $hintstr);
2092
2093
            // Process the list of IdPs to transform them appropriately.
2094
            foreach ($hintarray as &$value) {
2095
                // Check for 'chained' idp hints, and remove the GET params.
2096
                if (preg_match('%([^\?]*)\?%', $value, $matches)) {
2097
                    $value = $matches[1];
2098
                }
2099
                // Also, check for OIDC issuers and transform them into
2100
                // CILogon-specific values used in the 'Select an IdP' list.
2101
                if (preg_match('%https://accounts.google.com%', $value)) {
2102
                    $value = 'https://accounts.google.com/o/oauth2/auth';
2103
                } elseif (preg_match('%https://github.com%', $value)) {
2104
                    $value = 'https://github.com/login/oauth/authorize';
2105
                } elseif (preg_match('%https://orcid.org%', $value)) {
2106
                    $value = 'https://orcid.org/oauth/authorize';
2107
                }
2108
            }
2109
            unset($value); // Break the reference with the last element.
2110
2111
            // Remove any non-whitelisted IdPs from the hintarray.
2112
            if (empty($idps)) {
2113
                $idps = static::getCompositeIdPList();
2114
            }
2115
            foreach ($hintarray as $value) {
2116
                if (!isset($idps[$value])) {
2117
                    if (($key = array_search($value, $hintarray)) !== false) {
2118
                        unset($hintarray[$key]);
2119
                    }
2120
                }
2121
            }
2122
        }
2123
        return $hintarray;
2124
    }
2125
}
2126