Completed
Push — master ( d95b29...1c1079 )
by Terrence
23:33 queued 08:35
created

Content::getCompositeIdPList()   C

Complexity

Conditions 11
Paths 25

Size

Total Lines 61

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
dl 0
loc 61
ccs 0
cts 58
cp 0
rs 6.7042
c 0
b 0
f 0
cc 11
nc 25
nop 1
crap 132

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\Util;
6
use CILogon\Service\TwoFactor;
7
use CILogon\Service\MyProxy;
8
use CILogon\Service\PortalCookie;
9
use CILogon\Service\DBService;
10
use CILogon\Service\OAuth2Provider;
11
use CILogon\Service\Loggit;
12
use Net_LDAP2_Util;
13
14
// If needed, set the 'Notification' banner text to a non-empty value
15
// and uncomment the 'define' statement in order to display a
16
// notification box at the top of each page.
17
/*
18
define('BANNER_TEXT',
19
       'We are currently experiencing problems issuing certificates. We are
20
       working on a solution. We apologize for the inconvenience.'
21
);
22
*/
23
24
/**
25
 * Content
26
 */
27
class Content
28
{
29
    /**
30
     * printHeader
31
     *
32
     * This function should be called to print out the main HTML header
33
     * block for each web page.  This gives a consistent look to the site.
34
     * Any style changes should go in the cilogon.css file.
35
     *
36
     * @param string $title The text in the window's titlebar
37
     * @param string $extra Optional extra text to go in the <head> block
38
     * @param bool $csrfcookie Set the CSRF and CSRFProtetion cookies.
39
     *        Defaults to true.
40
     */
41
    public static function printHeader($title = '', $extra = '', $csrfcookie = true)
42
    {
43
        if ($csrfcookie) {
44
            $csrf = Util::getCsrf();
45
            $csrf->setTheCookie();
46
            // Set the CSRF cookie used by GridShib-CA
47
            Util::setCookieVar('CSRFProtection', $csrf->getTokenValue(), 0);
48
        }
49
50
        // Find the 'Powered By CILogon' image if specified by the skin
51
        $poweredbyimg = "/images/poweredbycilogon.png";
52
        $skin = Util::getSkin();
53
        $skinpoweredbyimg = (string)$skin->getConfigOption('poweredbyimg');
54
        if ((!is_null($skinpoweredbyimg)) &&
55
            (strlen($skinpoweredbyimg) > 0) &&
56
            (is_readable('/var/www/html' . $skinpoweredbyimg))) {
57
            $poweredbyimg = $skinpoweredbyimg;
58
        }
59
60
        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
61
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
62
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
63
        <head><title>' , $title , '</title>
64
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
65
        <meta name="viewport" content="initial-scale=0.6" />
66
        <link rel="stylesheet" type="text/css" href="/include/cilogon.css" />
67
        ';
68
69
        $skin->printSkinLink();
70
71
        $deployjava = $skin->getConfigOption('deployjava');
72
        if ((!is_null($deployjava)) && ((int)$deployjava == 1)) {
73
            echo '<script type="text/javascript" src="/include/deployJava.js"></script>';
74
        }
75
76
        echo '
77
        <script type="text/javascript" src="/include/cilogon.js"></script>
78
        ' ;
79
80
        echo '
81
    <!--[if IE]>
82
        <style type="text/css">
83
          body { behavior: url(/include/csshover3.htc); }
84
        </style>
85
    <![endif]-->
86
        ';
87
88
        if (strlen($extra) > 0) {
89
            echo $extra;
90
        }
91
92
        echo '
93
        </head>
94
95
        <body>
96
97
        <div class="skincilogonlogo">
98
        <a target="_blank" href="http://www.cilogon.org/faq/"><img
99
        src="' , $poweredbyimg , '" alt="CILogon"
100
        title="CILogon Service" /></a>
101
        </div>
102
103
        <div class="logoheader">
104
           <h1><span>[CILogon Service]</span></h1>
105
        </div>
106
        <div class="pagecontent">
107
         ';
108
109
        if ((defined('BANNER_TEXT')) && (strlen(BANNER_TEXT) > 0)) {
110
            echo '
111
            <div class="noticebanner">' , BANNER_TEXT , '</div>
112
            ';
113
        }
114
115
        $providerId = Util::getSessionVar('idp');
116
        if ($providerId == "urn:mace:incommon:idp.protectnetwork.org") {
117
            echo '
118
            <div class="noticebanner">Availability of the ProtectNetwork
119
            Identity Provider (IdP) will end after December 2014. Please
120
            consider using another IdP.</div>
121
            ';
122
        }
123
    }
124
125
    /**
126
     * printFooter
127
     *
128
     * This function should be called to print out the closing HTML block
129
     * for each web page.
130
     *
131
     * @param string $footer Optional extra text to be output before the
132
     * closing footer div.
133
     */
134
    public static function printFooter($footer = '')
135
    {
136
        if (strlen($footer) > 0) {
137
            echo $footer;
138
        }
139
140
        echo '
141
        <br class="clear" />
142
        <div class="footer">
143
        <a target="_blank" href="http://www.cilogon.org/faq"><img
144
        src="/images/questionIcon.png" class="floatrightclear"
145
        width="40" height="40" alt="CILogon FAQ" title="CILogon FAQ" /></a>
146
        <p>For questions about this site, please see the <a target="_blank"
147
        href="http://www.cilogon.org/faq">FAQs</a> or send email to <a
148
        href="mailto:[email protected]">help&nbsp;@&nbsp;cilogon.org</a>.</p>
149
        <p>Know <a target="_blank"
150
        href="http://ca.cilogon.org/responsibilities">your responsibilities</a>
151
        for using the CILogon Service.</p>
152
        <p>See <a target="_blank"
153
        href="http://ca.cilogon.org/acknowledgements">acknowledgements</a> of
154
        support for this site.</p>
155
        </div> <!-- Close "footer" div -->
156
        </div> <!-- Close "pagecontent" div -->
157
        </body>
158
        </html>
159
        ';
160
161
        session_write_close();
162
    }
163
164
    /**
165
     * printPageHeader
166
     *
167
     * This function prints a fancy formatted box with a single line of
168
     * text, suitable for a titlebox on each web page (to appear just below
169
     * the page banner at the very top). It prints a gradent border around
170
     * the four edges of the box and then outlines the inner box.
171
     *
172
     * @param string $text The text string to appear in the titlebox.
173
     */
174
    public static function printPageHeader($text)
175
    {
176
        echo '
177
        <div class="titlebox">' , $text , '
178
        </div>
179
        ';
180
    }
181
182
    /**
183
     * printFormHead
184
     *
185
     * This function prints out the opening <form> tag for displaying
186
     * submit buttons.  The first parameter is used for the 'action' value
187
     * of the <form>.  If omitted, getScriptDir() is called to get the
188
     * location of the current script.  This function outputs a hidden csrf
189
     * field in the form block.  If the second parameter is given and set
190
     * to true, then an additional hidden input element is output to be
191
     * utilized by the GridShib-CA client.
192
     *
193
     * @param string $action (Optional) The value of the form's 'action'
194
     *        parameter. Defaults to getScriptDir().
195
     * @param string $method (Optional) The <form> 'method', one of 'get' or
196
     *        'post'. Defaults to 'post'.
197
     * @param bool $gsca  (Optional) True if extra hidden tags should be
198
     *        output for the GridShib-CA client application.
199
     *        Defaults to false.
200
     */
201
    public static function printFormHead(
202
        $action = '',
203
        $method = 'post',
204
        $gsca = false
205
    ) {
206
        static $formnum = 0;
207
208
        if (strlen($action) == 0) {
209
            $action = Util::getScriptDir();
210
        }
211
212
        echo '
213
        <form action="' , $action , '" method="' , $method , '"
214
         autocomplete="off" id="form' , sprintf("%02d", ++$formnum) , '">
215
        ';
216
        $csrf = Util::getCsrf();
217
        echo $csrf->hiddenFormElement();
218
219
        if ($gsca) {
220
            // Output hidden form element for GridShib-CA
221
            echo '
222
            <input type="hidden" name="CSRFProtection" value="' .
223
            $csrf->getTokenValue() . '" />
224
            ';
225
        }
226
    }
227
228
    /**
229
     * printWAYF
230
     *
231
     * This function prints the list of IdPs in a <select> form element
232
     * which can be printed on the main login page to allow the user to
233
     * select 'Where Are You From?'.  This function checks to see if a
234
     * cookie for the 'providerId' had been set previously, so that the
235
     * last used IdP is selected in the list.
236
     *
237
     * @param bool $showremember (Optional) Show the 'Remember this
238
     *        selection' checkbox? Defaults to true.
239
     * @param bool $incommonidps (Optional) Show all InCommon IdPs in
240
     *        selection list? Defaults to false, which means show
241
     *        only whitelisted IdPs.
242
     */
243
    public static function printWAYF($showremember = true, $incommonidps = false)
244
    {
245
        $helptext = 'Check this box to bypass the welcome page on ' .
246
            'subsequent visits and proceed directly to the selected ' .
247
            'identity provider. You will need to clear your browser\'s ' .
248
            'cookies to return here.';
249
        $searchtext = "Enter characters to search for in the list above.";
250
251
        // Get an array of IdPs
252
        $idps = static::getCompositeIdPList($incommonidps);
253
254
        $skin = Util::getSkin();
255
256
        // Check if the user had previously selected an IdP from the list.
257
        // First, check the portalcookie, then the 'normal' cookie.
258
        $keepidp = '';
0 ignored issues
show
Unused Code introduced by
$keepidp is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
259
        $providerId = '';
0 ignored issues
show
Unused Code introduced by
$providerId is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
260
        $pc = new PortalCookie();
261
        $pn = $pc->getPortalName();
262
        if (strlen($pn) > 0) {
263
            $keepidp    = $pc->get('keepidp');
264
            $providerId = $pc->get('providerId');
265
        } else {
266
            $keepidp    = Util::getCookieVar('keepidp');
267
            $providerId = Util::getCookieVar('providerId');
268
        }
269
270
        // Make sure previously selected IdP is in list of available IdPs.
271
        if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
272
            $providerId = '';
273
        }
274
275
        // If no previous providerId, get from skin, or default to Google.
276
        if (strlen($providerId) == 0) {
277
            $initialidp = (string)$skin->getConfigOption('initialidp');
278
            if ((!is_null($initialidp)) && (isset($idps[$initialidp]))) {
279
                $providerId = $initialidp;
280
            } else {
281
                $providerId = Util::getAuthzUrl('Google');
282
            }
283
        }
284
285
        // Check if an OIDC client selected an IdP for the transaction.
286
        // If so, verify that the IdP is in the list of available IdPs.
287
        $useselectedidp = false;
288
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
289
        if (isset($clientparams['selected_idp'])) {
290
            $selected_idp = $clientparams['selected_idp'];
291
            if ((strlen($selected_idp) > 0) && (isset($idps[$selected_idp]))) {
292
                $useselectedidp = true;
293
                $providerId = $selected_idp;
294
                // Update the IdP selection list to show only this one IdP
295
                $idps = array($selected_idp => $idps[$selected_idp]);
296
            }
297
        }
298
299
        echo '
300
        <br />
301
        <div class="actionbox"';
302
303
        if (Util::getSessionVar('showhelp') == 'on') {
304
            echo ' style="width:92%;"';
305
        }
306
307
        echo '>
308
        <table class="helptable">
309
        <tr>
310
        <td class="actioncell">
311
312
          <form action="' , Util::getScriptDir() , '" method="post">
313
          <fieldset>
314
315
          <p>' , ($useselectedidp ? 'Selected' : 'Select An') ,
316
          ' Identity Provider:</p>
317
          ';
318
319
          // See if the skin has set a size for the IdP <select> list
320
          $selectsize = 4;
321
        $ils = $skin->getConfigOption('idplistsize');
322
        if ((!is_null($ils)) && ((int)$ils > 0)) {
323
            $selectsize = (int)$ils;
324
        }
325
326
        // When selected_idp is used, list size should always be 1.
327
        if ($useselectedidp) {
328
            $selectsize = 1;
329
        }
330
331
        echo '
332
          <p>
333
          <select name="providerId" id="providerId" size="' , $selectsize , '"
334
           onkeypress="enterKeySubmit(event)" ondblclick="doubleClickSubmit()"' ,
335
           // Hide the drop-down arrow in Firefox and Chrome
336
          ($useselectedidp ?
337
              'style="-moz-appearance:none;-webkit-appearance:none"' : '') ,
338
           '>
339
        ';
340
341
        foreach ($idps as $entityId => $names) {
342
            echo '    <option value="' , $entityId , '"';
343
            if ($entityId == $providerId) {
344
                echo ' selected="selected"';
345
            }
346
            echo '>' , Util::htmlent($names['Display_Name']) , '</option>' , "\n    ";
347
        }
348
349
        echo '  </select>
350
        </p>
351
352
        <p id="listsearch" class="zeroheight">
353
        <label for="searchlist" class="helpcursor" title="' ,
354
        $searchtext , '">Search:</label>
355
        <input type="text" name="searchlist" id="searchlist" value=""
356
        size="30" onkeyup="searchOptions(this.value)"
357
        title="' , $searchtext , '" />
358
    <!--[if IE]><input type="text" style="display:none;" disabled="disabled" size="1"/><![endif]-->
359
        </p>
360
        ';
361
362
        if ($showremember) {
363
            echo '
364
            <p>
365
            <label for="keepidp" title="' , $helptext ,
366
            '" class="helpcursor">Remember this selection:</label>
367
            <input type="checkbox" name="keepidp" id="keepidp" ' ,
368
            ((strlen($keepidp) > 0) ? 'checked="checked" ' : '') ,
369
            'title="' , $helptext , '" class="helpcursor" />
370
            </p>
371
            ';
372
        }
373
374
        echo '
375
        <p class="silvercheckbox">
376
        <label for="silveridp">Request Silver:</label>
377
        <input type="checkbox" name="silveridp" id="silveridp"/>
378
        </p>
379
380
        <p>
381
        ';
382
383
        echo Util::getCsrf()->hiddenFormElement();
384
385
        $lobtext = static::getLogOnButtonText();
386
387
        echo '
388
        <input type="submit" name="submit" class="submit helpcursor"
389
        title="Continue to the selected identity provider."
390
        value="' , $lobtext , '" id="wayflogonbutton" />
391
        <input type="hidden" name="previouspage" value="WAYF" />
392
        <input type="submit" name="submit" class="submit helpcursor"
393
        title="Cancel authentication and navigate away from this site."
394
        value="Cancel" id="wayfcancelbutton" />
395
        </p>
396
        ';
397
398
        $logonerror = Util::getSessionVar('logonerror');
399
        if (strlen($logonerror) > 0) {
400
            echo "<p class=\"logonerror\">$logonerror</p>";
401
            Util::unsetSessionVar('logonerror');
402
        }
403
404
        echo '
405
        <p class="privacypolicy">
406
        By selecting "' , $lobtext , '", you agree to <a target="_blank"
407
        href="http://ca.cilogon.org/policy/privacy">CILogon\'s privacy
408
        policy</a>.
409
        </p>
410
411
        </fieldset>
412
413
        </form>
414
      </td>
415
      ';
416
417
        if (Util::getSessionVar('showhelp') == 'on') {
418
            echo '
419
          <td class="helpcell">
420
          <div>
421
          ';
422
423
            if ($incommonidps) { // InCommon IdPs only means running from /testidp/
424
                echo '
425
                <p>
426
                CILogon facilitates secure access to CyberInfrastructure
427
                (<acronym title="CyberInfrastructure">CI</acronym>). In
428
                order to test your identity provider with the CILogon Service,
429
                you must first Log On. If your preferred identity provider is
430
                not listed, please contact <a
431
                href="mailto:[email protected]">[email protected]</a>, and
432
                we will try to add your identity provider in the future.
433
                </p>
434
                ';
435
            } else { // If not InCommon only, print help text for OpenID providers.
436
                echo '
437
                <p>
438
                CILogon facilitates secure access to CyberInfrastructure
439
                (<acronym title="CyberInfrastructure">CI</acronym>).
440
                In order to use the CILogon Service, you must first select
441
                an identity provider. An identity provider (IdP) is an
442
                organization where you have an account and can log on
443
                to gain access to online services.
444
                </p>
445
                <p>
446
                If you are a faculty, staff, or student member of a university
447
                or college, please select it for your identity provider.
448
                If your school is not listed, please contact <a
449
                href="mailto:[email protected]">[email protected]</a>, and we will
450
                try to add your school in the future.
451
                </p>
452
                ';
453
454
                $googleauthz = Util::getAuthzUrl('Google');
455
                if ((isset($idps[$googleauthz])) &&
456
                    ($skin->idpAvailable($googleauthz))) {
457
                    echo '
458
                  <p>
459
                  If you have a <a target="_blank"
460
                  href="https://myaccount.google.com">Google</a>
461
                  account, you can select it for
462
                  authenticating to the CILogon Service.
463
                  </p>
464
                  ';
465
                }
466
                $githubauthz = Util::getAuthzUrl('GitHub');
467
                if ((isset($idps[$githubauthz])) &&
468
                    ($skin->idpAvailable($githubauthz))) {
469
                    echo '
470
                  <p>
471
                  If you have a <a target="_blank"
472
                  href="https://github.com/settings/profile">GitHub</a>
473
                  account, you can select it for
474
                  authenticating to the CILogon Service.
475
                  </p>
476
                  ';
477
                }
478
                $orcidauthz = Util::getAuthzUrl('ORCID');
479
                if ((isset($idps[$orcidauthz])) &&
480
                    ($skin->idpAvailable($orcidauthz))) {
481
                    echo '
482
                  <p>
483
                  If you have a <a target="_blank"
484
                  href="https://orcid.org/my-orcid">ORCID</a>
485
                  account, you can select it for
486
                  authenticating to the CILogon Service.
487
                  </p>
488
                  ';
489
                }
490
            }
491
492
            echo '
493
          </div>
494
          </td>
495
          ';
496
        }
497
        echo '
498
      </tr>
499
      </table>
500
      </div>
501
      ';
502
    }
503
504
    /**
505
     * printTwoFactorBox
506
     *
507
     * This function prints the 'Manage Two-Factor' box on the main page.
508
     */
509
    public static function printTwoFactorBox()
510
    {
511
        $managetwofactortext = 'Enable or disable two-factor authentication for your account';
512
513
        echo '
514
        <div class="twofactoractionbox"';
515
516
        $style = ''; // Might add extra CSS to the twofactoractionbox
517
        if (Util::getSessionVar('showhelp') == 'on') {
518
            $style .= "width:92%;";
519
        }
520
        if (TwoFactor::getEnabled() != 'none') {
521
            $style .= "display:block;"; // Force display if two-factor enabled
522
        }
523
        if (strlen($style) > 0) {
524
            echo ' style="' , $style , '"';
525
        }
526
527
        echo '>
528
        <table class="helptable">
529
        <tr>
530
        <td class="actioncell">
531
        ';
532
533
        static::printFormHead();
534
535
        $twofactorname = TwoFactor::getEnabledName();
536
        if ($twofactorname == 'Disabled') {
537
            $twofactorname = 'Two-Factor Authentication Disabled';
538
        } else {
539
            $twofactorname .= ' Enabled';
540
        }
541
        echo '
542
          <p>' , $twofactorname , '</p>
543
544
          <p>
545
          <input type="submit" name="submit" class="submit helpcursor"
546
          title="' , $managetwofactortext , '" value="Manage Two-Factor" />
547
          </p>
548
          </form>
549
        </td>
550
        ';
551
552
        if (Util::getSessionVar('showhelp') == 'on') {
553
            echo '
554
            <td class="helpcell">
555
            <div>
556
            <p>
557
            Two-factor authentication provides extra security on your account by
558
            using a physical device (e.g., your mobile phone) to generate a one
559
            time password which you enter after you log in to your selected
560
            Identity Provider. Click the "Manage Two-Factor" button to enable or
561
            disable two-factor authentication.
562
            </p>
563
            </div>
564
            </td>
565
            ';
566
        }
567
568
        echo '
569
        </tr>
570
        </table>
571
        </div> <!-- twofactoractionbox -->
572
        ';
573
    }
574
575
    /**
576
     * printTwoFactorPage
577
     *
578
     * This function prints out the Manage Two-Factor Authentication page.
579
     * Display of which two-factor types are available to the user is
580
     * controlled by CSS. From this page, the user can Enable or Disable
581
     * various two-factor authentication methods.
582
     */
583
    public static function printTwoFactorPage()
584
    {
585
        Util::setSessionVar('stage', 'managetwofactor'); // For Show/Hide Help button
586
587
        static::printHeader('Manage Two-Factor Authentication');
588
589
        $twofactorname = TwoFactor::getEnabledName();
590
591
        echo '
592
        <div class="boxed">
593
        ';
594
        static::printHelpButton();
595
        echo'
596
        <h2>Two-Factor Authentication</h2>
597
        <div class="actionbox">
598
        <p><b>Two-Factor Authentication:</b></p>
599
        <p>' , $twofactorname , '</p>
600
        </div> <!-- actionbox -->
601
        ';
602
603
        if (Util::getSessionVar('showhelp') == 'on') {
604
            echo '
605
            <div>
606
            <p>
607
            Multi-factor authentication requires a user to present two or more
608
            distinct authentication factors from the following categories:
609
            </p>
610
            <ul>
611
            <li>Something you <b>know</b> (e.g., username and password)</li>
612
            <li>Something you <b>have</b> (e.g., mobile phone or hardware
613
                token)</li>
614
            <li>Something you <b>are</b> (e.g., fingerprint)</li>
615
            </ul>
616
            <p>
617
            Below you can configure a second factor using something you <b>have</b>,
618
            i.e., your mobile phone. You will first be prompted to register the
619
            second-factor device, typically by installing specific apps on your
620
            phone and completing a registration process. Then you will need to log
621
            in with the second factor to verify the registration process.
622
            </p>
623
            <p>
624
            Once you have successfully enabled two-factor authentication, you will
625
            be prompted on future CILogon Service logons for your second-factor
626
            authentication.  You can select the second-factor authentication to use
627
            (or not) by clicking the "Enable" (or "Disable") button.
628
            </p>
629
            </div>
630
            ';
631
        }
632
633
        echo '
634
        <table class="twofactortypes">
635
        <tr class="twofactorgooglerow"' ,
636
        (TwoFactor::isEnabled('ga') ? ' style="display:table-row;"' : '') ,
637
        '>
638
        <th>Google Authenticator</th>
639
        <td>
640
        ';
641
        static::printFormHead();
642
        echo '
643
        <input type="hidden" name="twofactortype" value="ga" />
644
        <input type="submit" name="submit" class="submit" value="' ,
645
        (TwoFactor::isEnabled('ga') ? 'Disable' : 'Enable') ,
646
        '" />
647
        </form>
648
        </td>
649
        </tr>
650
        ';
651
652
        if (Util::getSessionVar('showhelp') == 'on') {
653
            echo '
654
            <tr>
655
            <td colspan="4">
656
            Google Authenticator is an app available for Android OS, Apple
657
            iOS, and BlackBerry OS. The app generates one-time password tokens.
658
            After you have logged on to the CILogon Service with your chosen
659
            Identity Provider, you would be prompted to use the Google
660
            Authenticator app to generate a second passcode and enter it.
661
            </td>
662
            </tr>
663
            ';
664
        }
665
666
        echo '
667
        <tr class="twofactorduorow"' ,
668
        (TwoFactor::isEnabled('duo') ? '
669
            style="display:table-row;border-top-width:1px"' : '') ,
670
        '>
671
        <th>Duo Security</th>
672
        <td>
673
        ';
674
        static::printFormHead();
675
        echo '
676
        <input type="hidden" name="twofactortype" value="duo" />
677
        <input type="submit" name="submit" class="submit" value="' ,
678
        (TwoFactor::isEnabled('duo') ? 'Disable' : 'Enable') ,
679
        '" />
680
        </form>
681
        </td>
682
        </tr>
683
        ';
684
685
        if (Util::getSessionVar('showhelp') == 'on') {
686
            echo '
687
            <tr>
688
            <td colspan="4">
689
            Duo Security is an app available for most smartphones, including
690
            Android OS, Apple iOS, Blackberry OS, Palm, Symbian, and Windows
691
            Mobile. The app can respond to "push" notifications, and can also
692
            generate one-time password tokens. After you have logged on to the
693
            CILogon Service with your chosen Identity Provider, you would be
694
            prompted to select a Duo log in method and then authenticate with
695
            your mobile phone.
696
            </td>
697
            </tr>
698
            ';
699
        }
700
701
        echo '
702
        </table>
703
704
        <noscript>
705
        <div class="nojs smaller">
706
        Javascript is disabled. In order to activate the link
707
        below, please enable Javascript in your browser.
708
        </div>
709
        </noscript>
710
711
        <p>
712
        <a href="javascript:showHideDiv(\'lostdevice\',-1)">Lost your phone?</a>
713
        </p>
714
        <div id="lostdevice" style="display:none">
715
        <p>
716
        If you have lost your phone, you can click on the
717
        "I Lost My Phone" button below to remove all two-factor methods
718
        from your account.  This will send an email message to the system
719
        adminisrator and to the email address provided by your Identity
720
        Provider.  You can then use the CILogon Service without
721
        two-factor authentication enabled. You will need to re-register your
722
        device with you want to use it for two-factor authentication again.
723
        <br/>
724
        ';
725
        static::printFormHead();
726
        echo '
727
        <input type="submit" name="submit" class="submit"
728
        value="I Lost My Phone" />
729
        </p>
730
        </div>
731
732
        <p>
733
        <input type="submit" name="submit" class="submit"
734
        value="Done with Two-Factor" />
735
        </p>
736
        </form>
737
738
        </div> <!-- boxed -->
739
        ';
740
        static::printFooter();
741
    }
742
743
    /**
744
     * handleEnableDisableTwoFactor
745
     *
746
     * This function is called when the user clicks either an 'Enable' or
747
     * 'Disable' button from the Manage Two-Factor page, or when the user
748
     * clicks 'Verify' on the Google Authenticator Registration page.
749
     * The passed-in parameter tells which type of button was pressed.
750
     * If 'Disable', then simply set 'enabled=none' in the datastore and
751
     * display the Manage Two-Factor page again. If 'Enable' or 'Verify',
752
     * check the 'twofactortype' hidden form variable for which two-factor
753
     * authentication method is to be enabled. Then print out that
754
     * two-factor page. Note that TwoFactor::printPage() does the work of
755
     * figuring out if the user has registered the phone yet or not, and
756
     * displays the appropriate page.
757
     *
758
     * @param bool $enable (Optional) True for 'enable', false for 'disable'.
759
     *        Defaults to false (for 'disable').
760
     */
761
    public static function handleEnableDisableTwoFactor($enable = false)
762
    {
763
        if ($enable) {
764
            $twofactortype = Util::getPostVar('twofactortype');
765
            if (strlen($twofactortype) > 0) {
766
                TwoFactor::printPage($twofactortype);
767
            } else {
768
                printLogonPage();
769
            }
770
        } else { // 'Disable' clicked
771
            // Check if the user clicked 'Disable Two-Factor' and send email
772
            if (Util::getPostVar('missingphone') == '1') {
773
                // Make sure two-factor was enabled
774
                $twofactorname = TwoFactor::getEnabledName();
775
                if ($twofactorname != 'Disabled') {
776
                    $email = static::getEmailFromDN(Util::getSessionVar('dn'));
777
                    if (strlen($email) > 0) { // Make sure email address exists
778
                        TwoFactor::sendPhoneAlert(
779
                            'Forgot Phone for Two-Factor Authentication',
780
                            'While using the CILogon Service, you (or someone using your account)
781
indicated that you forgot your phone by clicking the "Disable Two-Factor"
782
button. This disabled two-factor authentication by "' . $twofactorname . '"
783
using "' . Util::getSessionVar('idpname') . '" as your Identity Provider.
784
785
If you did not disable two-factor authentication, please send email to
786
"[email protected]" to report this incident.',
787
                            $email
788
                        );
789
                    } else { // No email address is bad - send error alert
790
                        Util::sendErrorAlert(
791
                            'Missing Email Address',
792
                            'When attempting to send an email notification to a user who clicked the
793
"Disable Two-Factor" button because of a forgotten phone, the CILogon
794
Service was unable to find an email address. This should never occur and
795
is probably due to a badly formatted "dn" string.'
796
                        );
797
                    }
798
                }
799
            }
800
801
            // Finally, disable two-factor authentication
802
            TwoFactor::setDisabled();
803
            TwoFactor::write();
804
            static::printTwoFactorPage();
805
        }
806
    }
807
808
    /**
809
     * handleILostMyPhone
810
     *
811
     * This function is called when the user clicks the 'I Lost My Phone'
812
     * button.  It sends email to the user AND to alerts because Duo
813
     * Security requires that a sysadmin unregister the phone for the user.
814
     * It then unsets the 'twofactor' session variable, and writes it to
815
     * the datastore, effectively wiping out all two-factor information for
816
     * the user.
817
     */
818
    public static function handleILostMyPhone()
819
    {
820
        // First, send email to user
821
        $email = static::getEmailFromDN(Util::getSessionVar('dn'));
822
        if (strlen($email) > 0) { // Make sure email address exists
823
            TwoFactor::sendPhoneAlert(
824
                'Lost Phone for Two-Factor Authentication',
825
                'While using the CILogon Service, you (or someone using your account)
826
indicated that you lost your phone by clicking the "I Lost My Phone"
827
button. This removed two-factor authentication for your account when
828
using "' . Util::getSessionVar('idpname') . '" as your Identity Provider.
829
830
System administrators have been notified of this incident. If you require
831
further assistance, please send email to "[email protected]".',
832
                $email
833
            );
834
        } else { // No email address is bad - send error alert
835
            Util::sendErrorAlert(
836
                'Missing Email Address',
837
                'When attempting to send an email notification to a user who clicked the
838
"I Lost My Phone" button, the CILogon Service was unable to find an
839
email address. This should never occur and is probably due to a badly
840
formatted "dn" string.'
841
            );
842
        }
843
844
        // Next, send email to sysadmin
845
        $errortext = 'A user clicked the "I Lost My Phone" button. ';
846
        if (TwoFactor::isRegistered('duo')) {
847
            $duoconfig = new DuoConfig();
848
            $errortext .= '
849
850
The user had registered "Duo Security" as one of the two-factor methods.
851
Since there is no way for the CILogon Service to UNregister this method
852
at the Duo Security servers, a system administrator will need to delete
853
this user\'s registration at https://' . $duoconfig->param['host'] . ' .';
854
        }
855
        Util::sendErrorAlert('Two-Factor Authentication Disabled', $errortext);
856
857
        // Finally, disable and unregister two-factor authentication
858
        Util::unsetSessionVar('twofactor');
859
        TwoFactor::write();
860
        static::printTwoFactorPage();
861
    }
862
863
    /**
864
     * handleGoogleAuthenticatorLogin
865
     *
866
     * This function is called when the user enters a one time password as
867
     * generated by the Google Authenticator app. This can occur (1) when
868
     * the user is first configuring GA two-factor and (2) when the user
869
     * logs in to the CILogon Service and GA is enabled. If the OTP is
870
     * correctly validated, the gotUserSuccess() function is called to
871
     * show output to the user.
872
     */
873
    public static function handleGoogleAuthenticatorLogin()
874
    {
875
        $gacode = Util::getPostVar('gacode');
876
        if ((strlen($gacode) > 0) && (TwoFactor::isGACodeValid($gacode))) {
877
            static::gotUserSuccess();
878
        } else {
879
            TwoFactor::printPage('ga');
880
        }
881
    }
882
883
    /**
884
     * handleDuoSecurityLogin
885
     *
886
     * This function is called when the user authenticates with Duo
887
     * Security. If the Duo authentication is valid, then the
888
     * gotUserSuccess() function is then called to show output to the user.
889
     */
890
    public static function handleDuoSecurityLogin()
891
    {
892
        $sig_response = Util::getPostVar('sig_response');
893
        if ((strlen($sig_response) > 0) &&
894
            (TwoFactor::isDuoCodeValid($sig_response))) {
895
            static::gotUserSuccess();
896
        } else {
897
            TwoFactor::printPage('duo');
898
        }
899
    }
900
901
    /**
902
     * handleLogOnButtonClicked
903
     *
904
     * This function is called when the user clicks the 'Log On' button
905
     * on the IdP selection page. It checks to see if the 'Remember this
906
     * selection' checkbox was checked and sets a cookie appropriately. It
907
     * also sets a cookie 'providerId' so the last chosen IdP will be
908
     * selected the next time the user visits the site. The function then
909
     * calls the appropriate 'redirectTo...' function to send the user
910
     * to the chosen IdP.
911
     */
912
    public static function handleLogOnButtonClicked()
913
    {
914
        // Get the list of currently available IdPs
915
        $idps = static::getCompositeIdPList();
916
917
        // Set the cookie for keepidp if the checkbox was checked
918
        $pc = new PortalCookie();
919
        $pn = $pc->getPortalName();
920
        if (strlen(Util::getPostVar('keepidp')) > 0) {
921
            if (strlen($pn) > 0) {
922
                $pc->set('keepidp', 'checked');
923
            } else {
924
                Util::setCookieVar('keepidp', 'checked');
925
            }
926
        } else {
927
            if (strlen($pn) > 0) {
928
                $pc->set('keepidp', '');
929
            } else {
930
                Util::unsetCookieVar('keepidp');
931
            }
932
        }
933
934
        // Get the user-chosen IdP from the posted form
935
        $providerId = Util::getPostVar('providerId');
936
937
        // Set the cookie for the last chosen IdP and redirect to it if in list
938
        if ((strlen($providerId) > 0) && (isset($idps[$providerId]))) {
939
            if (strlen($pn) > 0) {
940
                $pc->set('providerId', $providerId);
941
                $pc->write();
942
            } else {
943
                Util::setCookieVar('providerId', $providerId);
944
            }
945
            $providerName = Util::getAuthzIdP($providerId);
946
            if (in_array($providerName, Util::$oauth2idps)) {
947
                // Log in with an OAuth2 IdP
948
                static::redirectToGetOAuth2User($providerId);
949
            } else { // Use InCommon authn
950
                static::redirectToGetShibUser($providerId);
951
            }
952
        } else { // IdP not in list, or no IdP selected
953
            if (strlen($pn) > 0) {
954
                $pc->set('providerId', '');
955
                $pc->write();
956
            } else {
957
                Util::unsetCookieVar('providerId');
958
            }
959
            Util::setSessionVar('logonerror', 'Please select a valid IdP.');
960
            printLogonPage();
961
        }
962
    }
963
964
    /**
965
     * handleHelpButtonClicked
966
     *
967
     * This function is called when the user clicks on the 'Show Help' /
968
     * 'Hide Help' button in the upper right corner of the page. It toggles
969
     * the 'showhelp' session variable and redisplays the appropriate page
970
     * with help now shown or hidden.
971
     */
972
    public static function handleHelpButtonClicked()
973
    {
974
        if (Util::getSessionVar('showhelp') == 'on') {
975
            Util::unsetSessionVar('showhelp');
976
        } else {
977
            Util::setSessionVar('showhelp', 'on');
978
        }
979
980
        $stage = Util::getSessionVar('stage');
981
        if (static::verifyCurrentUserSession()) {
982
            if ($stage == 'main') {
983
                printMainPage();
984
            } elseif ($stage == 'managetwofactor') {
985
                static::printTwoFactorPage();
986
            } else {
987
                printLogonPage();
988
            }
989
        } else {
990
            printLogonPage();
991
        }
992
    }
993
994
    /**
995
     * handleNoSubmitButtonClicked
996
     *
997
     * This function is the 'default' case when no 'submit' button has been
998
     * clicked, or if the submit session variable is not set. It checks
999
     * to see if either the <forceinitialidp> option is set, or if the
1000
     * 'Remember this selection' checkbox was previously checked. If so,
1001
     * then rediret to the appropriate IdP. Otherwise, print the main
1002
     * Log On page.
1003
     */
1004
    public static function handleNoSubmitButtonClicked()
1005
    {
1006
        $providerId = '';
1007
        $keepidp = '';
1008
        $selected_idp = '';
1009
        $redirect_uri = '';
1010
        $callbackuri = Util::getSessionVar('callbackuri');
1011
        $readidpcookies = true;  // Assume config options are not set
1012
        $skin = Util::getSkin();
1013
        $forceinitialidp = (int)$skin->getConfigOption('forceinitialidp');
1014
        $initialidp = (string)$skin->getConfigOption('initialidp');
1015
1016
        // If this is a OIDC transaction, get the selected_idp and
1017
        // redirect_uri parameters from the session var clientparams.
1018
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1019
        if (isset($clientparams['selected_idp'])) {
1020
            $selected_idp = $clientparams['selected_idp'];
1021
        }
1022
        if (isset($clientparams['redirect_uri'])) {
1023
            $redirect_uri = $clientparams['redirect_uri'];
1024
        }
1025
1026
        // CIL-431 - If the OAuth2/OIDC $redirect_uri is set, then check for
1027
        // a match in the 'bypass.txt' file to see if we should
1028
        // automatically redirect to a specific IdP. Used mainly by campus
1029
        // gateways.
1030
        if (strlen($redirect_uri) > 0) {
1031
            $bypassidp = '';
1032
            $bypassarray = Util::readArrayFromFile(
1033
                Util::getServerVar('DOCUMENT_ROOT') . '/include/bypass.txt'
1034
            );
1035
            foreach ($bypassarray as $key => $value) {
1036
                if (preg_match($key, $redirect_uri)) {
1037
                    $bypassidp = $value;
1038
                    break;
1039
                }
1040
            }
1041
            if (strlen($bypassidp) > 0) { // Match found!
1042
                $providerId = $bypassidp;
1043
                $keepidp = 'checked';
1044
                // To skip the next code blocks, unset a few variables.
1045
                $forceinitialidp = 0;     // Skip checking this option
1046
                $selected_idp = '';       // Skip any passed-in option
1047
                $readidpcookies = false;  // Don't read in the IdP cookies
1048
            }
1049
        }
1050
1051
        // If the <forceinitialidp> option is set, use either the
1052
        // <initialidp> or the 'selected_idp' as the providerId, and
1053
        // use <forceinitialidp> as keepIdp. Otherwise, read the
1054
        // cookies 'providerId' and 'keepidp'.
1055
        if (($forceinitialidp == 1) &&
1056
            ((strlen($initialidp) > 0) || (strlen($selected_idp) > 0))) {
1057
            // If the <allowforceinitialidp> option is set, then make sure
1058
            // the callback / redirect uri is in the portal list.
1059
            $afii=$skin->getConfigOption('portallistaction', 'allowforceinitialidp');
1060
            if ((is_null($afii)) || // Option not set, no need to check portal list
1061
                (((int)$afii == 1) &&
1062
                  (($skin->inPortalList($callbackuri)) ||
1063
                   ($skin->inPortalList($redirect_uri))))) {
1064
                // 'selected_idp' takes precedence over <initialidp>
1065
                if (strlen($selected_idp) > 0) {
1066
                    $providerId = $selected_idp;
1067
                } else {
1068
                    $providerId = $initialidp;
1069
                }
1070
                $keepidp = $forceinitialidp;
1071
                $readidpcookies = false; // Don't read in the IdP cookies
1072
            }
1073
        }
1074
1075
        // <initialidp> options not set, or portal not in portal list?
1076
        // Get idp and 'Remember this selection' from cookies instead.
1077
        $pc = new PortalCookie();
1078
        $pn = $pc->getPortalName();
1079
        if ($readidpcookies) {
1080
            // Check the portalcookie first, then the 'normal' cookies
1081
            if (strlen($pn) > 0) {
1082
                $keepidp    = $pc->get('keepidp');
1083
                $providerId = $pc->get('providerId');
1084
            } else {
1085
                $keepidp    = Util::getCookieVar('keepidp');
1086
                $providerId = Util::getCookieVar('providerId');
1087
            }
1088
        }
1089
1090
        // If both 'keepidp' and 'providerId' were set (and the
1091
        // providerId is a whitelisted IdP or valid OpenID provider),
1092
        // then skip the Logon page and proceed to the appropriate
1093
        // getuser script.
1094
        if ((strlen($providerId) > 0) && (strlen($keepidp) > 0)) {
1095
            // If selected_idp was specified at the OIDC authorize endpoint,
1096
            // make sure that it matches the saved providerId. If not,
1097
            // then show the Logon page and uncheck the keepidp checkbox.
1098
            if ((strlen($selected_idp) == 0) || ($selected_idp == $providerId)) {
1099
                $providerName = Util::getAuthzIdP($providerId);
1100
                if (in_array($providerName, Util::$oauth2idps)) {
1101
                    // Log in with an OAuth2 IdP
1102
                    static::redirectToGetOAuth2User($providerId);
1103
                } elseif (Util::getIdpList()->exists($providerId)) {
1104
                    // Log in with InCommon
1105
                    static::redirectToGetShibUser($providerId);
1106
                } else { // $providerId not in whitelist
1107
                    if (strlen($pn) > 0) {
1108
                        $pc->set('providerId', '');
1109
                        $pc->write();
1110
                    } else {
1111
                        Util::unsetCookieVar('providerId');
1112
                    }
1113
                    printLogonPage();
1114
                }
1115
            } else { // selected_idp does not match saved providerId
1116
                if (strlen($pn) > 0) {
1117
                    $pc->set('keepidp', '');
1118
                    $pc->write();
1119
                } else {
1120
                    Util::unsetCookieVar('keepidp');
1121
                }
1122
                printLogonPage();
1123
            }
1124
        } else { // One of providerId or keepidp was not set
1125
            printLogonPage();
1126
        }
1127
    }
1128
1129
    /**
1130
     * printIcon
1131
     *
1132
     * This function prints out the HTML for the little icons which can
1133
     * appear inline with other information.  This is accomplished via the
1134
     * use of wrapping the image in a <span> tag.
1135
     *
1136
     * @param string $icon The prefix of the '...Icon.png' image to be
1137
     *        shown. E.g., to show 'errorIcon.png', pass in 'error'.
1138
     * @param string $popuptext (Optionals) The popup 'title' text to be
1139
     *        displayed when the  mouse cursor hovers over the icon.
1140
     *        Defaults to empty string.
1141
     * @param string $class (Optionals) A CSS class for the icon. Will be
1142
     *        appended after the 'helpcursor' class. Defaults to empty
1143
     *        string.
1144
     */
1145
    public static function printIcon($icon, $popuptext = '', $class = '')
1146
    {
1147
        echo '<span';
1148
        if (strlen($popuptext) > 0) {
1149
            echo ' class="helpcursor ' , $class , '" title="' , $popuptext , '"';
1150
        }
1151
        echo '>&nbsp;<img src="/images/' , $icon , 'Icon.png"
1152
              alt="&laquo; ' , ucfirst($icon) , '"
1153
              width="14" height="14" /></span>';
1154
    }
1155
1156
    /**
1157
     * printHelpButton
1158
     *
1159
     * This function prints the 'Show Help' / 'Hide Help' button in the
1160
     * upper-right corner of the main box area on the page.
1161
     */
1162
    public static function printHelpButton()
1163
    {
1164
        echo '
1165
        <div class="helpbutton">
1166
        ';
1167
1168
        static::printFormHead();
1169
1170
        echo '
1171
          <input type="submit" name="submit" class="helpbutton" value="' ,
1172
          (Util::getSessionVar('showhelp')=='on' ? 'Hide':'Show') , '&#10; Help " />
1173
          </form>
1174
        </div>
1175
        ';
1176
    }
1177
1178
    /**
1179
     * verifyCurrentUserSession
1180
     *
1181
     * This function verifies the contents of the PHP session.  It checks
1182
     * the following:
1183
     * (1) The persistent store 'uid', the Identity Provider 'idp', the
1184
     *     IdP Display Name 'idpname', and the 'status' (of getUser()) are
1185
     *     all non-empty strings.
1186
     * (2) The 'status' (of getUser()) is even (i.e. STATUS_OK).
1187
     * (3) If $providerId is passed-in, it must match 'idp'.
1188
     * If all checks are good, then this function returns true.
1189
     *
1190
     * @param string $providerId (Optional) The user-selected Identity
1191
     *        Provider. If set, make sure $providerId matches the PHP
1192
     *        session variable 'idp'.
1193
     * @return bool True if the contents of the PHP session ar valid.
1194
     *              False otherwise.
1195
     */
1196
    public static function verifyCurrentUserSession($providerId = '')
1197
    {
1198
        $retval = false;
1199
1200
        // Check for eduGAIN IdP and possible get cert context
1201
        if (static::isEduGAINAndGetCert()) {
1202
            Util::unsetUserSessionVars();
1203
        }
1204
1205
        $idp       = Util::getSessionVar('idp');
1206
        $idpname   = Util::getSessionVar('idpname');
1207
        $uid       = Util::getSessionVar('uid');
1208
        $status    = Util::getSessionVar('status');
1209
        $dn        = Util::getSessionVar('dn');
1210
        $authntime = Util::getSessionVar('authntime');
1211
1212
1213
        if ((strlen($uid) > 0) && (strlen($idp) > 0) &&
1214
            (strlen($idpname) > 0) && (strlen($status) > 0) &&
1215
            (strlen($dn) > 0) && (strlen($authntime) > 0) &&
1216
            (!($status & 1))) {  // All STATUS_OK codes are even
1217
            if ((strlen($providerId) == 0) || ($providerId == $idp)) {
1218
                $retval = true;
1219
            }
1220
        }
1221
1222
        // As a final check, see if the IdP requires a forced skin
1223
        if ($retval) {
1224
            Util::getSkin()->init();
1225
        }
1226
1227
        return $retval;
1228
    }
1229
1230
    /**
1231
     * redirectToGetShibUser
1232
     *
1233
     * This method redirects control flow to the getuser script for
1234
     * If the first parameter (a whitelisted entityId) is not specified,
1235
     * we check to see if either the providerId PHP session variable or the
1236
     * providerId cookie is set (in that order) and use one if available.
1237
     * The function then checks to see if there is a valid PHP session
1238
     * and if the providerId matches the 'idp' in the session.  If so, then
1239
     * we don't need to redirect to '/secure/getuser/' and instead we
1240
     * we display the main page.  However, if the PHP session is not valid,
1241
     * then this function redirects to the '/secure/getuser/' script so as
1242
     * to do a Shibboleth authentication via mod_shib. When the providerId
1243
     * is non-empty, the SessionInitiator will automatically go to that IdP
1244
     * (i.e. without stopping at a WAYF).  This function also sets
1245
     * several PHP session variables that are needed by the getuser script,
1246
     * including the 'responsesubmit' variable which is set as the return
1247
     * 'submit' variable in the 'getuser' script.
1248
     *
1249
     * @param string $providerId (Optional) An entityId of the
1250
     *        authenticating IdP. If not specified (or set to the empty
1251
     *        string), we check providerId PHP session variable and
1252
     *        providerId cookie (in that order) for non-empty values.
1253
     * @param string $responsesubmit (Optional) The value of the PHP session
1254
     *       'submit' variable to be set upon return from the 'getuser'
1255
     *        script.  This is utilized to control the flow of this script
1256
     *        after 'getuser'. Defaults to 'gotuser'.
1257
     * @param string responseurl (Optional) A response url for redirection
1258
     *        after successful processing at /secure/getuser/. Defaults to
1259
     *        the current script directory.
1260
     * @param bool $allowsilver Is it okay to request silver assurance in
1261
     *        the authnContextClassRef? If not, then ignore the 'Request
1262
     *        Silver' checkbox and silver certification in metadata.
1263
     *        Defaults to true.
1264
     */
1265
    public static function redirectToGetShibUser(
1266
        $providerId = '',
1267
        $responsesubmit = 'gotuser',
1268
        $responseurl = null,
1269
        $allowsilver = true
1270
    ) {
1271
1272
        // If providerId not set, try the cookie value
1273
        if (strlen($providerId) == 0) {
1274
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
1275
        }
1276
1277
        // If the user has a valid 'uid' in the PHP session, and the
1278
        // providerId matches the 'idp' in the PHP session, then
1279
        // simply go to the main page.
1280
        if (static::verifyCurrentUserSession($providerId)) {
1281
            printMainPage();
1282
        } else { // Otherwise, redirect to the getuser script
1283
            // Set PHP session varilables needed by the getuser script
1284
            Util::setSessionVar(
1285
                'responseurl',
1286
                (is_null($responseurl) ?
1287
                    Util::getScriptDir(true) : $responseurl)
1288
            );
1289
            Util::setSessionVar('submit', 'getuser');
1290
            Util::setSessionVar('responsesubmit', $responsesubmit);
1291
            Util::getCsrf()->setCookieAndSession();
1292
1293
            // Set up the 'header' string for redirection thru mod_shib
1294
            $redirect =
1295
                'Location: https://' . static::getMachineHostname() .
1296
                '/Shibboleth.sso/Login?' .
1297
                'target=' . urlencode(
1298
                    'https://' . static::getMachineHostname() .
1299
                    '/secure/getuser/'
1300
                );
1301
1302
            if (strlen($providerId) > 0) {
1303
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
1304
                if ($providerId == 'urn:mace:incommon:nih.gov') {
1305
                    $redirect = preg_replace(
1306
                        '%/Shibboleth.sso/Login%',
1307
                        '/Shibboleth.sso/NIHLogin',
1308
                        $redirect
1309
                    );
1310
                }
1311
1312
                $redirect .= '&providerId=' . urlencode($providerId);
1313
1314
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
1315
                $forceauthn = Util::getSessionVar('forceauthn');
1316
                Util::unsetSessionVar('forceauthn');
1317
                if ($forceauthn) {
1318
                    $redirect .= '&forceAuthn=true';
1319
                } elseif (strlen($forceauthn)==0) {
1320
                    // 'forceauth' was not set to '0' in the session, so
1321
                    // check the skin's option instead.
1322
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
1323
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
1324
                        $redirect .= '&forceAuthn=true';
1325
                    }
1326
                }
1327
1328
                // If Silver IdP or 'Request Silver' checked, send extra parameter
1329
                if ($allowsilver) {
1330
                    if ((Util::getIdpList()->isSilver($providerId)) ||
1331
                        (strlen(Util::getPostVar('silveridp')) > 0)) {
1332
                        Util::setSessionVar('requestsilver', '1');
1333
                        $redirect .= '&authnContextClassRef=' .
1334
                            urlencode('http://id.incommon.org/assurance/silver');
1335
                    }
1336
                }
1337
            }
1338
1339
            $log = new Loggit();
1340
            $log->info('Shibboleth Login="' . $redirect . '"');
1341
            header($redirect);
1342
            exit; // No further processing necessary
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectToGetShibUser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1343
        }
1344
    }
1345
1346
    /**
1347
     * redirectToGetOAuth2User
1348
     *
1349
     * This method redirects control flow to the getuser script for
1350
     * when the user logs in via OAuth 2.0. It first checks to see
1351
     * if we have a valid session. If so, we don't need to redirect and
1352
     * instead simply show the Get Certificate page. Otherwise, we start
1353
     * an OAuth 2.0 logon by composing a parameterized GET URL using
1354
     * the OAuth 2.0 endpoint.
1355
     *
1356
     * @param string $providerId (Optional) An entityId of the
1357
     *        authenticating IdP. If not specified (or set to the empty
1358
     *        string), we check providerId PHP session variable and
1359
     *        providerId cookie (in that order) for non-empty values.
1360
     * @param string $responsesubmit (Optional) The value of the PHP session
1361
     *        'submit' variable to be set upon return from the 'getuser'
1362
     *         script.  This is utilized to control the flow of this script
1363
     *         after 'getuser'. Defaults to 'gotuser'.
1364
     */
1365
    public static function redirectToGetOAuth2User(
1366
        $providerId = '',
1367
        $responsesubmit = 'gotuser'
1368
    ) {
1369
        // If providerId not set, try the cookie value
1370
        if (strlen($providerId) == 0) {
1371
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
1372
        }
1373
1374
        // If the user has a valid 'uid' in the PHP session, and the
1375
        // providerId matches the 'idp' in the PHP session, then
1376
        // simply go to the 'Download Certificate' button page.
1377
        if (static::verifyCurrentUserSession($providerId)) {
1378
            printMainPage();
1379
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
1380
            // Set PHP session varilables needed by the getuser script
1381
            Util::unsetSessionVar('logonerror');
1382
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
1383
            Util::setSessionVar('submit', 'getuser');
1384
            Util::setSessionVar('responsesubmit', $responsesubmit);
1385
            $csrf = Util::getCsrf();
1386
            $csrf->setCookieAndSession();
1387
            $extraparams = array();
1388
            $extraparams['state'] = $csrf->getTokenValue();
1389
1390
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
1391
            $forceauthn = Util::getSessionVar('forceauthn');
1392
            Util::unsetSessionVar('forceauthn');
1393
            if ($forceauthn) {
1394
                $extraparams['approval_prompt'] = 'force';
1395
            } elseif (strlen($forceauthn)==0) {
1396
                // 'forceauth' was not set to '0' in the session, so
1397
                // check the skin's option instead.
1398
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
1399
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
1400
                    $extraparams['approval_prompt'] = 'force';
1401
                }
1402
            }
1403
1404
            // Get the provider name based on the provider authz URL
1405
            $providerName = Util::getAuthzIdP($providerId);
1406
1407
            // Get the authz URL and redirect
1408
            $oauth2 = new OAuth2Provider($providerName);
1409
            if (is_null($oauth2->provider)) {
1410
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
1411
                printLogonPage();
1412
            } else {
1413
                $authUrl = $oauth2->provider->getAuthorizationUrl(
1414
                    array_merge(
1415
                        $oauth2->authzUrlOpts,
1416
                        $extraparams
1417
                    )
1418
                );
1419
                header('Location: ' . $authUrl);
1420
                exit; // No further processing necessary
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectToGetOAuth2User() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1421
            }
1422
        }
1423
    }
1424
1425
    /**
1426
     * printErrorBox
1427
     *
1428
     * This function prints out a bordered box with an error icon and any
1429
     * passed-in error HTML text.  The error icon and text are output to
1430
     * a <table> so as to keep the icon to the left of the error text.
1431
     *
1432
     * @param string $errortext HTML error text to be output
1433
     */
1434
    public static function printErrorBox($errortext)
1435
    {
1436
        echo '
1437
        <div class="errorbox">
1438
        <table cellpadding="5">
1439
        <tr>
1440
        <td valign="top">
1441
        ';
1442
        static::printIcon('error');
1443
        echo '&nbsp;
1444
        </td>
1445
        <td> ' , $errortext , '
1446
        </td>
1447
        </tr>
1448
        </table>
1449
        </div>
1450
        ';
1451
    }
1452
1453
    /**
1454
     * handleGotUser
1455
     *
1456
     * This function is called upon return from one of the getuser scripts
1457
     * which should have set the 'uid' and 'status' PHP session variables.
1458
     * It verifies that the status return is one of STATUS_OK (even
1459
     * values).  If not, we print an error message to the user.
1460
     */
1461
    public static function handleGotUser()
1462
    {
1463
        $log = new Loggit();
1464
        $uid = Util::getSessionVar('uid');
1465
        $status = Util::getSessionVar('status');
1466
1467
        // We must get and unset session vars BEFORE any HTML output since
1468
        // a redirect may go to another site, meaning we need to update
1469
        // the session cookie before we leave the cilogon.org domain.
1470
        $ePPN         = Util::getSessionVar('ePPN');
1471
        $ePTID        = Util::getSessionVar('ePTID');
1472
        $firstname    = Util::getSessionVar('firstname');
1473
        $lastname     = Util::getSessionVar('lastname');
1474
        $displayname  = Util::getSessionVar('displayname');
1475
        $emailaddr    = Util::getSessionVar('emailaddr');
1476
        $idp          = Util::getSessionVar('idp');
1477
        $idpname      = Util::getSessionVar('idpname');
1478
        $affiliation  = Util::getSessionVar('affiliation');
1479
        $ou           = Util::getSessionVar('ou');
1480
        $memberof     = Util::getSessionVar('memberof');
1481
        $acr          = Util::getSessionVar('acr');
1482
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1483
        $failureuri   = Util::getSessionVar('failureuri');
1484
1485
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
1486
        // If found, set 'Proceed' button redirect appropriately.
1487
        $redirect = '';
1488
        $redirectform = '';
1489
        // First, check for OIDC redirect_uri, with parameters in <form>
1490
        if (isset($clientparams['redirect_uri'])) {
1491
            $redirect = $clientparams['redirect_uri'];
1492
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
1493
                '<input type="hidden" name="error_description" value="Missing attributes" />';
1494
            if (isset($clientparams['state'])) {
1495
                $redirectform .= '<input type="hidden" name="state" value="' .
1496
                    $clientparams['state'] . '" />';
1497
            }
1498
        }
1499
        // Next, check for OAuth 1.0a
1500
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
1501
            $redirect = $failureuri. "?reason=missing_attributes";
1502
        }
1503
1504
        // If empty 'uid' or 'status' or odd-numbered status code, error!
1505
        if ((strlen($uid) == 0) || (strlen($status) == 0) || ($status & 1)) {
1506
            // Got all session vars by now, so okay to unset.
1507
            Util::unsetAllUserSessionVars();
1508
1509
            $log->error('Failed to getuser.');
1510
1511
            static::printHeader('Error Logging On');
1512
1513
            echo '
1514
            <div class="boxed">
1515
            ';
1516
1517
            if ($status == DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) {
1518
                // Check if the problem IdP was an OAuth2 IdP;
1519
                // probably no first/last name
1520
                if ($idpname == 'Google') {
1521
                    static::printErrorBox('
1522
                    <p>
1523
                    There was a problem logging on. It appears that you have
1524
                    attempted to use Google as your identity provider, but your
1525
                    name or email address was missing. To rectify this problem,
1526
                    go to the <a target="_blank"
1527
                    href="https://myaccount.google.com/privacy#personalinfo">Google
1528
                    Account Personal Information page</a>, and enter your first
1529
                    name, last name, and email address. (All other Google
1530
                    account information is not required by the CILogon Service.)
1531
                    </p>
1532
                    <p>
1533
                    After you have updated your Google account profile, click
1534
                    the "Proceed" button below and attempt to log on
1535
                    with your Google account again. If you have any questions,
1536
                    please contact us at the email address at the bottom of the
1537
                    page.</p>
1538
                    ');
1539
1540
                    echo '
1541
                    <div>
1542
                    ';
1543
                    static::printFormHead($redirect, 'get');
1544
                    echo '
1545
                    <p class="centered">
1546
                    <input type="hidden" name="providerId" value="' ,
1547
                    Util::getAuthzUrl('Google') , '" /> ' , $redirectform , '
1548
                    <input type="submit" name="submit" class="submit"
1549
                    value="Proceed" />
1550
                    </p>
1551
                    </form>
1552
                    </div>
1553
                    ';
1554
                } elseif ($idpname == 'GitHub') {
1555
                    static::printErrorBox('
1556
                    <p>
1557
                    There was a problem logging on. It appears that you have
1558
                    attempted to use GitHub as your identity provider, but your
1559
                    name or email address was missing. To rectify this problem,
1560
                    go to the <a target="_blank"
1561
                    href="https://github.com/settings/profile">GitHub
1562
                    Public Profile page</a>, and enter your name and email address.
1563
                    (All other GitHub account information is not required by
1564
                    the CILogon Service.)
1565
                    </p>
1566
                    <p>
1567
                    After you have updated your GitHub account profile, click
1568
                    the "Proceed" button below and attempt to log on
1569
                    with your GitHub account again. If you have any questions,
1570
                    please contact us at the email address at the bottom of the
1571
                    page.</p>
1572
                    ');
1573
1574
                    echo '
1575
                    <div>
1576
                    ';
1577
                    static::printFormHead($redirect, 'get');
1578
                    echo '
1579
                    <p class="centered">
1580
                    <input type="hidden" name="providerId" value="' ,
1581
                    Util::getAuthzUrl('GitHub') , '" /> ' , $redirectform , '
1582
                    <input type="submit" name="submit" class="submit"
1583
                    value="Proceed" />
1584
                    </p>
1585
                    </form>
1586
                    </div>
1587
                    ';
1588
                } elseif ($idpname == 'ORCID') {
1589
                    static::printErrorBox('
1590
                    <p>
1591
                    There was a problem logging on. It appears that you have
1592
                    attempted to use ORCID as your identity provider, but your
1593
                    name or email address was missing. To rectify this problem,
1594
                    go to your <a target="_blank"
1595
                    href="https://orcid.org/my-orcid">ORCID
1596
                    Profile page</a>, enter your name and email address, and
1597
                    make sure they can be viewed by Everyone.
1598
                    (All other ORCID account information is not required by
1599
                    the CILogon Service.)
1600
                    </p>
1601
                    <p>
1602
                    After you have updated your ORCID account profile, click
1603
                    the "Proceed" button below and attempt to log on
1604
                    with your ORCID account again. If you have any questions,
1605
                    please contact us at the email address at the bottom of the
1606
                    page.</p>
1607
                    ');
1608
1609
                    echo '
1610
                    <div>
1611
                    ';
1612
                    static::printFormHead($redirect, 'get');
1613
                    echo '
1614
                    <p class="centered">
1615
                    <input type="hidden" name="providerId" value="' ,
1616
                    Util::getAuthzUrl('ORCID') , '" /> ' , $redirectform , '
1617
                    <input type="submit" name="submit" class="submit"
1618
                    value="Proceed" />
1619
                    </p>
1620
                    </form>
1621
                    </div>
1622
                    ';
1623
                } else { // Problem was missing SAML attribute from Shib IdP
1624
                    static::printAttributeReleaseErrorMessage(
1625
                        $ePPN,
1626
                        $ePTID,
1627
                        $firstname,
1628
                        $lastname,
1629
                        $displayname,
1630
                        $emailaddr,
1631
                        $idp,
1632
                        $idpname,
1633
                        $affiliation,
1634
                        $ou,
1635
                        $memberof,
1636
                        $acr,
1637
                        $clientparams,
1638
                        $redirect,
1639
                        $redirectform
1640
                    );
1641
                }
1642
            } else {
1643
                static::printErrorBox('An internal error has occurred. System
1644
                    administrators have been notified. This may be a temporary
1645
                    error. Please try again later, or contact us at the the email
1646
                    address at the bottom of the page.');
1647
1648
                echo '
1649
                <div>
1650
                ';
1651
                static::printFormHead($redirect, 'get');
1652
                echo $redirectform , '
1653
                <input type="submit" name="submit" class="submit" value="Proceed" />
1654
                </form>
1655
                </div>
1656
                ';
1657
            }
1658
1659
            echo '
1660
            </div>
1661
            ';
1662
            static::printFooter();
1663
        } elseif (static::isEduGAINAndGetCert($idp, $idpname)) {
1664
            // If eduGAIN IdP and session can get a cert, then error!
1665
            // Got all session vars by now, so okay to unset.
1666
            Util::unsetAllUserSessionVars();
1667
1668
            $log->error('Failed to getuser due to eduGAIN IdP restriction.');
1669
1670
            static::printHeader('Error Logging On');
1671
1672
            echo '
1673
            <div class="boxed">
1674
            ';
1675
            static::printAttributeReleaseErrorMessage(
1676
                $ePPN,
1677
                $ePTID,
1678
                $firstname,
1679
                $lastname,
1680
                $displayname,
1681
                $emailaddr,
1682
                $idp,
1683
                $idpname,
1684
                $affiliation,
1685
                $ou,
1686
                $memberof,
1687
                $acr,
1688
                $clientparams,
1689
                $redirect,
1690
                $redirectform
1691
            );
1692
1693
            echo '
1694
            </div>
1695
            ';
1696
            static::printFooter();
1697
        } else { // Got one of the STATUS_OK status codes
1698
            // Extra security check: Once the user has successfully authenticated
1699
            // with an IdP, verify that the chosen IdP was actually whitelisted.
1700
            // If not, then set error message and show Select an Identity Provider
1701
            // page again.
1702
            Util::getSkin()->init();  // Check for forced skin
1703
            $idps = static::getCompositeIdPList();
1704
            $providerId = Util::getSessionVar('idp');
1705
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
1706
                Util::setSessionVar(
1707
                    'logonerror',
1708
                    'Invalid IdP selected. Please try again.'
1709
                );
1710
                Util::sendErrorAlert(
1711
                    'Authentication attempt using non-whitelisted IdP',
1712
                    'A user successfully authenticated with an IdP, however, the
1713
selected IdP was not in the list of whitelisted IdPs as determined
1714
by the current skin. This might indicate the user attempted to
1715
circumvent the security check in "handleGotUser()" for valid
1716
IdPs for the skin.'
1717
                );
1718
                Util::unsetCookieVar('providerId');
1719
                Util::unsetAllUserSessionVars();
1720
                printLogonPage();
1721
            } else { // Check if two-factor authn is enabled and proceed accordingly
1722
                if (TwoFactor::getEnabled() == 'none') {
1723
                    static::gotUserSuccess();
1724
                } else {
1725
                    TwoFactor::printPage();
1726
                }
1727
            }
1728
        }
1729
    }
1730
1731
    /**
1732
     * gotUserSuccess
1733
     *
1734
     * This function is called after the user has been successfully
1735
     * authenticated. In the case of two-factor authentication, the user
1736
     * is first authenticated by the IdP, and then by the configured
1737
     * two-factor authentication method. If the 'status' session variable is
1738
     * STATUS_OK then it checks if we have a new or changed user and prints
1739
     * that page as appropriate. Otherwise it continues to the MainPage.
1740
     */
1741
    public static function gotUserSuccess()
1742
    {
1743
        $status = Util::getSessionVar('status');
1744
1745
        // If this is the first time the user has used the CILogon Service, we
1746
        // skip the New User page under the following circumstances.
1747
        // (1) We are using the OIDC authorization endpoint code flow (check for
1748
        // 'clientparams' session variable);
1749
        // (2) We are using the 'delegate' code flow (check for 'callbackuri'
1750
        // session variable), and one of the following applies:
1751
        //    (a) Skin has 'forceremember' set or
1752
        //    (b) Skin has 'initialremember' set and there is no cookie for the
1753
        //        current portal
1754
        // In these cases, we skip the New User page and proceed directly to the
1755
        // main page. Note that we still want to show the User Changed page to
1756
        // inform the user about updated DN strings.
1757
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1758
        $callbackuri = Util::getSessionVar('callbackuri');
1759
        $skin = Util::getSkin();
1760
        $forceremember = $skin->getConfigOption('delegate', 'forceremember');
1761
1762
        if (($status == DBService::$STATUS['STATUS_NEW_USER']) &&
1763
            ((strlen($callbackuri) > 0) ||
1764
             (isset($clientparams['code'])))) {
1765
            // Extra check for new users: see if any HTML entities
1766
            // are in the user name. If so, send an email alert.
1767
            $dn = Util::getSessionVar('dn');
1768
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
1769
            $htmldn = Util::htmlent($dn);
1770
            if (strcmp($dn, $htmldn) != 0) {
1771
                Util::sendErrorAlert(
1772
                    'New user DN contains HTML entities',
1773
                    "htmlentites(DN) = $htmldn\n"
1774
                );
1775
            }
1776
1777
            if (isset($clientparams['code'])) {
1778
                // OIDC authorization code flow always skips New User page
1779
                $status = DBService::$STATUS['STATUS_OK'];
1780
            } elseif (strlen($callbackuri) > 0) {
1781
                // Delegation code flow might skip New User page
1782
                if ((!is_null($forceremember)) && ((int)$forceremember == 1)) {
1783
                    // Check forcerememeber skin option to skip new user page
1784
                    $status = DBService::$STATUS['STATUS_OK'];
1785
                } else {
1786
                    // Check initialremember skin option PLUS no portal cookie
1787
                    $initialremember =
1788
                        $skin->getConfigOption('delegate', 'initialremember');
1789
                    if ((!is_null($initialremember)) && ((int)$initialremember==1)) {
1790
                        $pc = new PortalCookie();
1791
                        $portallifetime = $pc->get('lifetime');
1792
                        if ((strlen($portallifetime)==0) || ($portallifetime==0)) {
1793
                            $status = DBService::$STATUS['STATUS_OK'];
1794
                        }
1795
                    }
1796
                }
1797
            }
1798
        }
1799
1800
        // If the user got a new DN due to changed SAML attributes,
1801
        // print out a notification page.
1802
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
1803
            static::printNewUserPage();
1804
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
1805
            static::printUserChangedPage();
1806
        } else { // STATUS_OK
1807
            printMainPage();
1808
        }
1809
    }
1810
1811
    /**
1812
     * printNewUserPage
1813
     *
1814
     * This function prints out a notification page to new users showing
1815
     * that this is the first time they have logged in with a particular
1816
     * identity provider.
1817
     */
1818
    public static function printNewUserPage()
1819
    {
1820
        $log = new Loggit();
1821
        $log->info('New User page.');
1822
1823
        $dn = Util::getSessionVar('dn');
1824
        $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
1825
1826
        static::printHeader('New User');
1827
1828
        echo '
1829
        <div class="boxed">
1830
        <br class="clear"/>
1831
        <p>
1832
        Welcome! Your new certificate subject is as follows.
1833
        </p>
1834
        <p>
1835
        <blockquote><tt>' , Util::htmlent($dn) , '</tt></blockquote>
1836
        </p>
1837
        <p>
1838
        You may need to register this certificate subject with relying parties.
1839
        </p>
1840
        <p>
1841
        You will not see this page again unless the CILogon Service assigns you
1842
        a new certificate subject. This may occur in the following situations:
1843
        </p>
1844
        <ul>
1845
        <li>You log on to the CILogon Service using an identity provider other
1846
        than ' , Util::getSessionVar('idpname') , '.
1847
        </li>
1848
        <li>You log on using a different ' , Util::getSessionVar('idpname') , '
1849
        identity.
1850
        </li>
1851
        <li>The CILogon Service has experienced an internal error.
1852
        </li>
1853
        </ul>
1854
        <p>
1855
        Click the "Proceed" button to continue. If you have any questions,
1856
        please contact us at the email address at the bottom of the page.
1857
        </p>
1858
        <div>
1859
        ';
1860
        static::printFormHead();
1861
        echo '
1862
        <p class="centered">
1863
        <input type="submit" name="submit" class="submit" value="Proceed" />
1864
        </p>
1865
        </form>
1866
        </div>
1867
        </div>
1868
        ';
1869
        static::printFooter();
1870
    }
1871
1872
    /**
1873
     * printUserChangedPage
1874
     *
1875
     * This function prints out a notification page informing the user that
1876
     * some of their attributes have changed, which will affect the
1877
     * contents of future issued certificates.  This page shows which
1878
     * attributes are different (displaying both old and new values) and
1879
     * what portions of the certificate are affected.
1880
     */
1881
    public static function printUserChangedPage()
1882
    {
1883
        $errstr = '';
1884
1885
        $log = new Loggit();
1886
        $log->info('User IdP attributes changed.');
1887
1888
        $uid = Util::getSessionVar('uid');
1889
        $dbs = new DBService();
1890
        if (($dbs->getUser($uid)) &&
1891
            (!($dbs->status & 1))) {  // STATUS_OK codes are even
1892
            $idpname = $dbs->idp_display_name;
1893
            $first   = $dbs->first_name;
1894
            $last    = $dbs->last_name;
1895
            $email   = $dbs->email;
1896
            $dn      = $dbs->distinguished_name;
1897
            $dn      = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
1898
1899
            if (($dbs->getLastArchivedUser($uid)) &&
1900
                (!($dbs->status & 1))) {  // STATUS_OK codes are even
1901
                $previdpname = $dbs->idp_display_name;
1902
                $prevfirst   = $dbs->first_name;
1903
                $prevlast    = $dbs->last_name;
1904
                $prevemail   = $dbs->email;
1905
                $prevdn      = $dbs->distinguished_name;
1906
                $prevdn      = static::reformatDN(
1907
                    preg_replace(
1908
                        '/\s+email=.+$/',
1909
                        '',
1910
                        $prevdn
1911
                    )
1912
                );
1913
1914
                $tablerowodd = true;
1915
1916
                static::printHeader('Certificate Information Changed');
1917
1918
                echo '
1919
                <div class="boxed">
1920
                <br class="clear"/>
1921
                <p>
1922
                One or more of the attributes released by your organization has
1923
                changed since the last time you logged on to the CILogon
1924
                Service. This will affect your certificates as described below.
1925
                </p>
1926
1927
                <div class="userchanged">
1928
                <table cellpadding="5">
1929
                  <tr class="headings">
1930
                    <th>Attribute</th>
1931
                    <th>Previous Value</th>
1932
                    <th>Current Value</th>
1933
                  </tr>
1934
                ';
1935
1936
                if ($idpname != $previdpname) {
1937
                    echo '
1938
                    <tr' , ($tablerowodd ? ' class="odd"' : '') , '>
1939
                      <th>Organization Name:</th>
1940
                      <td>'.$previdpname.'</td>
1941
                      <td>'.$idpname.'</td>
1942
                    </tr>
1943
                    ';
1944
                    $tablerowodd = !$tablerowodd;
1945
                }
1946
1947
                if ($first != $prevfirst) {
1948
                    echo '
1949
                    <tr' , ($tablerowodd ? ' class="odd"' : '') , '>
1950
                      <th>First Name:</th>
1951
                      <td>'.Util::htmlent($prevfirst).'</td>
1952
                      <td>'.Util::htmlent($first).'</td>
1953
                    </tr>
1954
                    ';
1955
                    $tablerowodd = !$tablerowodd;
1956
                }
1957
1958
                if ($last != $prevlast) {
1959
                    echo '
1960
                    <tr' , ($tablerowodd ? ' class="odd"' : '') , '>
1961
                      <th>Last Name:</th>
1962
                      <td>'.Util::htmlent($prevlast).'</td>
1963
                      <td>'.Util::htmlent($last).'</td>
1964
                    </tr>
1965
                    ';
1966
                    $tablerowodd = !$tablerowodd;
1967
                }
1968
1969
                if ($email != $prevemail) {
1970
                    echo '
1971
                    <tr' , ($tablerowodd ? ' class="odd"' : '') , '>
1972
                      <th>Email Address:</th>
1973
                      <td>'.$prevemail.'</td>
1974
                      <td>'.$email.'</td>
1975
                    </tr>
1976
                    ';
1977
                    $tablerowodd = !$tablerowodd;
0 ignored issues
show
Unused Code introduced by
$tablerowodd is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1978
                }
1979
1980
                echo '
1981
                </table>
1982
                </div>
1983
                ';
1984
1985
                if (($idpname != $previdpname) ||
1986
                    ($first != $prevfirst) ||
1987
                    ($last != $prevlast)) {
1988
                    echo '
1989
                    <p>
1990
                    The above changes to your attributes will cause your
1991
                    <strong>certificate subject</strong> to change. You may be
1992
                    required to re-register with relying parties using this new
1993
                    certificate subject.
1994
                    </p>
1995
                    <p>
1996
                    <blockquote>
1997
                    <table cellspacing="0">
1998
                      <tr>
1999
                        <td>Previous Subject DN:</td>
2000
                        <td>' , Util::htmlent($prevdn) , '</td>
2001
                      </tr>
2002
                      <tr>
2003
                        <td>Current Subject DN:</td>
2004
                        <td>' , Util::htmlent($dn) , '</td>
2005
                      </tr>
2006
                    </table>
2007
                    </blockquote>
2008
                    </p>
2009
                    ';
2010
                }
2011
2012
                if ($email != $prevemail) {
2013
                    echo '
2014
                    <p>
2015
                    Your new certificate will contain your <strong>updated email
2016
                    address</strong>.
2017
                    This may change how your certificate may be used in email
2018
                    clients. Possible problems which may occur include:
2019
                    </p>
2020
                    <ul>
2021
                    <li>If your "from" address does not match what is
2022
                        contained in the certificate, recipients may fail to
2023
                        verify your signed email messages.</li>
2024
                    <li>If the email address in the certificate does not
2025
                        match the destination address, senders may have
2026
                        difficulty encrypting email addressed to you.</li>
2027
                    </ul>
2028
                    ';
2029
                }
2030
2031
                echo '
2032
                <p>
2033
                If you have any questions, please contact us at the email
2034
                address at the bottom of the page.
2035
                </p>
2036
                <div>
2037
                ';
2038
                static::printFormHead();
2039
                echo '
2040
                <p class="centered">
2041
                <input type="submit" name="submit" class="submit"
2042
                value="Proceed" />
2043
                </p>
2044
                </form>
2045
                </div>
2046
                </div>
2047
                ';
2048
                static::printFooter();
2049
            } else {  // Database error, should never happen
2050
                if (!is_null($dbs->status)) {
2051
                    $errstr = array_search($dbs->status, DBService::$STATUS);
2052
                }
2053
                $log->error('Database error reading last archived ' .
2054
                                  'user attributes. ' . $errstr);
2055
                Util::sendErrorAlert(
2056
                    'dbService Error',
2057
                    'Error calling dbservice action "getLastArchivedUser" in ' .
2058
                    'printUserChangedPaged() method. ' . $errstr
2059
                );
2060
                Util::unsetAllUserSessionVars();
2061
                printLogonPage();
2062
            }
2063
        } else {  // Database error, should never happen
2064
            if (!is_null($dbs->status)) {
2065
                $errstr = array_search($dbs->status, DBService::$STATUS);
2066
            }
2067
            $log->error('Database error reading current user attributes. ' .
2068
                              $errstr);
2069
            Util::sendErrorAlert(
2070
                'dbService Error',
2071
                'Error calling dbservice action "getUser" in ' .
2072
                'printUserChangedPaged() method. ' . $errstr
2073
            );
2074
            Util::unsetAllUserSessionVars();
2075
            printLogonPage();
2076
        }
2077
    }
2078
2079
    /**
2080
     * generateP12
2081
     *
2082
     * This function is called when the user clicks the 'Get New
2083
     * Certificate' button. It first reads in the password fields and
2084
     * verifies that they are valid (i.e. they are long enough and match).
2085
     * Then it gets a credential from the MyProxy server and converts that
2086
     * certificate into a PKCS12 which is written to disk.  If everything
2087
     * succeeds, the temporary pkcs12 directory and lifetime is saved to
2088
     * the 'p12' PHP session variable, which is read later when the Main
2089
     * Page HTML is shown.
2090
     */
2091
    public static function generateP12()
2092
    {
2093
        $log = new Loggit();
2094
2095
        // Get the entered p12lifetime and p12multiplier and set the cookies
2096
        list($minlifetime, $maxlifetime) =
2097
            static::getMinMaxLifetimes('pkcs12', 9516);
2098
        $p12lifetime   = Util::getPostVar('p12lifetime');
2099
        $p12multiplier = Util::getPostVar('p12multiplier');
2100
        if (strlen($p12multiplier) == 0) {
2101
            $p12multiplier = 1;  // For ECP, p12lifetime is in hours
2102
        }
2103
        $lifetime = $p12lifetime * $p12multiplier;
2104
        if ($lifetime <= 0) { // In case user entered negative number
2105
            $lifetime = $maxlifetime;
2106
            $p12lifetime = $maxlifetime;
2107
            $p12multiplier = 1;  // maxlifetime is in hours
2108
        } elseif ($lifetime < $minlifetime) {
2109
            $lifetime = $minlifetime;
2110
            $p12lifetime = $minlifetime;
2111
            $p12multiplier = 1;  // minlifetime is in hours
2112
        } elseif ($lifetime > $maxlifetime) {
2113
            $lifetime = $maxlifetime;
2114
            $p12lifetime = $maxlifetime;
2115
            $p12multiplier = 1;  // maxlifetime is in hours
2116
        }
2117
        Util::setCookieVar('p12lifetime', $p12lifetime);
2118
        Util::setCookieVar('p12multiplier', $p12multiplier);
2119
        Util::setSessionVar('p12lifetime', $p12lifetime);
2120
        Util::setSessionVar('p12multiplier', $p12multiplier);
2121
2122
        // Verify that the password is at least 12 characters long
2123
        $password1 = Util::getPostVar('password1');
2124
        $password2 = Util::getPostVar('password2');
2125
        $p12password = Util::getPostVar('p12password');  // For ECP clients
2126
        if (strlen($p12password) > 0) {
2127
            $password1 = $p12password;
2128
            $password2 = $p12password;
2129
        }
2130
        if (strlen($password1) < 12) {
2131
            Util::setSessionVar(
2132
                'p12error',
2133
                'Password must have at least 12 characters.'
2134
            );
2135
            return; // SHORT PASSWORD - NO FURTHER PROCESSING NEEDED!
2136
        }
2137
2138
        // Verify that the two password entry fields matched
2139
        if ($password1 != $password2) {
2140
            Util::setSessionVar('p12error', 'Passwords did not match.');
2141
            return; // MISMATCHED PASSWORDS - NO FURTHER PROCESSING NEEDED!
2142
        }
2143
2144
        // Set the port based on the Level of Assurance
2145
        $port = 7512;
2146
        $loa = Util::getSessionVar('loa');
2147
        if ($loa == 'http://incommonfederation.org/assurance/silver') {
2148
            $port = 7514;
2149
        } elseif ($loa == 'openid') {
2150
            $port = 7516;
2151
        }
2152
2153
        $dn = Util::getSessionVar('dn');
2154
        if (strlen($dn) > 0) {
2155
            // Append extra info, such as 'skin', to be processed by MyProxy
2156
            $myproxyinfo = Util::getSessionVar('myproxyinfo');
2157
            if (strlen($myproxyinfo) > 0) {
2158
                $dn .= " $myproxyinfo";
2159
            }
2160
            // Attempt to fetch a credential from the MyProxy server
2161
            $cert = MyProxy::getMyProxyCredential(
2162
                $dn,
2163
                '',
2164
                'myproxy.cilogon.org,myproxy2.cilogon.org',
2165
                $port,
2166
                $lifetime,
2167
                '/var/www/config/hostcred.pem',
2168
                ''
2169
            );
2170
2171
            // The 'openssl pkcs12' command is picky in that the private
2172
            // key must appear BEFORE the public certificate. But MyProxy
2173
            // returns the private key AFTER. So swap them around.
2174
            $cert2 = '';
2175
            if (preg_match(
2176
                '/-----BEGIN CERTIFICATE-----([^-]+)' .
2177
                '-----END CERTIFICATE-----[^-]*' .
2178
                '-----BEGIN RSA PRIVATE KEY-----([^-]+)' .
2179
                '-----END RSA PRIVATE KEY-----/',
2180
                $cert,
2181
                $match
2182
            )) {
2183
                $cert2 = "-----BEGIN RSA PRIVATE KEY-----" .
2184
                         $match[2] . "-----END RSA PRIVATE KEY-----\n".
2185
                         "-----BEGIN CERTIFICATE-----" .
2186
                         $match[1] . "-----END CERTIFICATE-----";
2187
            }
2188
2189
            if (strlen($cert2) > 0) { // Successfully got a certificate!
2190
                // Create a temporary directory in /var/www/html/pkcs12/
2191
                $tdirparent = '/var/www/html/pkcs12/';
2192
                $polonum = '3';   // Prepend the polo? number to directory
2193
                if (preg_match('/(\d+)\./', php_uname('n'), $polomatch)) {
2194
                    $polonum = $polomatch[1];
2195
                }
2196
                $tdir = Util::tempDir($tdirparent, $polonum, 0770);
2197
                $p12dir = str_replace($tdirparent, '', $tdir);
2198
                $p12file = $tdir . '/usercred.p12';
2199
2200
                // Call the openssl pkcs12 program to convert certificate
2201
                exec('/bin/env ' .
2202
                     'RANDFILE=/tmp/.rnd ' .
2203
                     'CILOGON_PKCS12_PW=' . escapeshellarg($password1) . ' ' .
2204
                     '/usr/bin/openssl pkcs12 -export ' .
2205
                     '-passout env:CILOGON_PKCS12_PW ' .
2206
                     "-out $p12file " .
2207
                     '<<< ' . escapeshellarg($cert2));
2208
2209
                // Verify the usercred.p12 file was actually created
2210
                $size = @filesize($p12file);
2211
                if (($size !== false) && ($size > 0)) {
2212
                    $p12link = 'https://' . static::getMachineHostname() .
2213
                               '/pkcs12/' . $p12dir . '/usercred.p12';
2214
                    $p12 = (time()+300) . " " . $p12link;
2215
                    Util::setSessionVar('p12', $p12);
2216
                    $log->info('Generated New User Certificate="'.$p12link.'"');
2217
                } else { // Empty or missing usercred.p12 file - shouldn't happen!
2218
                    Util::setSessionVar(
2219
                        'p12error',
2220
                        'Error creating certificate. Please try again.'
2221
                    );
2222
                    Util::deleteDir($tdir); // Remove the temporary directory
2223
                    $log->info('Error creating certificate - missing usercred.p12');
2224
                }
2225
            } else { // The myproxy-logon command failed - shouldn't happen!
2226
                Util::setSessionVar(
2227
                    'p12error',
2228
                    'Error! MyProxy unable to create certificate.'
2229
                );
2230
                $log->info('Error creating certificate - myproxy-logon failed');
2231
            }
2232
        } else { // Couldn't find the 'dn' PHP session value - shouldn't happen!
2233
            Util::setSessionVar(
2234
                'p12error',
2235
                'Missing username. Please enable cookies.'
2236
            );
2237
            $log->info('Error creating certificate - missing dn session variable');
2238
        }
2239
    }
2240
2241
    /**
2242
     * getLogOnButtonText
2243
     *
2244
     * This function checks the current skin to see if <logonbuttontext>
2245
     * has been configured.  If so, it returns that value.  Otherwise,
2246
     * it returns 'Log On'.
2247
     *
2248
     * @return string The text of the 'Log On' button for the WAYF, as
2249
     *         configured for the skin.  Defaults to 'Log On'.
2250
     */
2251
    public static function getLogOnButtonText()
2252
    {
2253
        $retval = 'Log On';
2254
        $lobt = Util::getSkin()->getConfigOption('logonbuttontext');
2255
        if (!is_null($lobt)) {
2256
            $retval = (string)$lobt;
2257
        }
2258
        return $retval;
2259
    }
2260
2261
    /**
2262
     * getSerialStringFromDN
2263
     *
2264
     * This function takes in a CILogon subject DN and returns just the
2265
     * serial string part (e.g., A325). This function is needed since the
2266
     * serial_string is not stored in the PHP session as a separate
2267
     * variable since it is always available in the 'dn' session variable.
2268
     *
2269
     * @param string $dn The certificate subject DN (typically found in the
2270
     *        session 'dn' variable)
2271
     * @return string The serial string extracted from the subject DN, or
2272
     *         empty string if DN is empty or wrong format.
2273
     */
2274
    public static function getSerialStringFromDN($dn)
2275
    {
2276
        $serial = ''; // Return empty string upon error
2277
2278
        // Strip off the email address, if present
2279
        $dn = preg_replace('/\s+email=.+$/', '', $dn);
2280
        // Find the 'CN=' entry
2281
        if (preg_match('%/DC=org/DC=cilogon/C=US/O=.*/CN=(.*)%', $dn, $match)) {
2282
            $cn = $match[1];
2283
            if (preg_match('/\s+([^\s]+)$/', $cn, $match)) {
2284
                $serial = $match[1];
2285
            }
2286
        }
2287
        return $serial;
2288
    }
2289
2290
    /**
2291
     * getEmailFromDN
2292
     *
2293
     * This function takes in a CILogon subject DN and returns just the
2294
     * email address part. This function is needed since the email address
2295
     * is not stored in the PHP session as a separate variable since it is
2296
     * always available in the 'dn' session variable.
2297
     *
2298
     * @param string $dn The certificate subject DN (typically found in the
2299
     *        session 'dn' variable)
2300
     * @return string The email address extracted from the subject DN, or
2301
     *         empty string if DN is empty or wrong format.
2302
     */
2303
    public static function getEmailFromDN($dn)
2304
    {
2305
        $email = ''; // Return empty string upon error
2306
        if (preg_match('/\s+email=(.+)$/', $dn, $match)) {
2307
            $email = $match[1];
2308
        }
2309
        return $email;
2310
    }
2311
2312
    /**
2313
     * reformatDN
2314
     *
2315
     * This function takes in a certificate subject DN with the email=...
2316
     * part already removed. It checks the skin to see if <dnformat> has
2317
     * been set. If so, it reformats the DN appropriately.
2318
     *
2319
     * @param string $dn The certificate subject DN (without the email=... part)
2320
     * @return string The certificate subject DN transformed according to
2321
     *         the value of the <dnformat> skin config option.
2322
     */
2323
    public static function reformatDN($dn)
2324
    {
2325
        $newdn = $dn;
2326
        $dnformat = (string)Util::getSkin()->getConfigOption('dnformat');
2327
        if (!is_null($dnformat)) {
2328
            if (($dnformat == 'rfc2253') &&
2329
                (preg_match(
2330
                    '%/DC=(.*)/DC=(.*)/C=(.*)/O=(.*)/CN=(.*)%',
2331
                    $dn,
2332
                    $match
2333
                ))) {
2334
                array_shift($match);
2335
                $m = array_reverse(Net_LDAP2_Util::escape_dn_value($match));
2336
                $newdn = "CN=$m[0],O=$m[1],C=$m[2],DC=$m[3],DC=$m[4]";
2337
            }
2338
        }
2339
        return $newdn;
2340
    }
2341
2342
    /**
2343
     * getMinMaxLifetimes
2344
     *
2345
     * This function checks the skin's configuration to see if either or
2346
     * both of minlifetime and maxlifetime in the specified config.xml
2347
     * block have been set. If not, default to minlifetime of 1 (hour) and
2348
     * the specified defaultmaxlifetime.
2349
     *
2350
     * @param string $section The XML section block from which to read the
2351
     *        minlifetime and maxlifetime values. Can be one of the
2352
     *        following: 'pkcs12', 'gsca', or 'delegate'.
2353
     * @param int $defaultmaxlifetime Default maxlifetime (in hours) for the
2354
     *        credential.
2355
     * @return array An array consisting of two entries: the minimum and
2356
     *         maximum lifetimes (in hours) for a credential.
2357
     */
2358
    public static function getMinMaxLifetimes($section, $defaultmaxlifetime)
2359
    {
2360
        $minlifetime = 1;    // Default minimum lifetime is 1 hour
2361
        $maxlifetime = $defaultmaxlifetime;
2362
        $skin = Util::getSkin();
2363
        $skinminlifetime = $skin->getConfigOption($section, 'minlifetime');
2364
        // Read the skin's minlifetime value from the specified section
2365
        if ((!is_null($skinminlifetime)) && ((int)$skinminlifetime > 0)) {
2366
            $minlifetime = max($minlifetime, (int)$skinminlifetime);
2367
            // Make sure $minlifetime is less than $maxlifetime;
2368
            $minlifetime = min($minlifetime, $maxlifetime);
2369
        }
2370
        // Read the skin's maxlifetime value from the specified section
2371
        $skinmaxlifetime = $skin->getConfigOption($section, 'maxlifetime');
2372
        if ((!is_null($skinmaxlifetime)) && ((int)$skinmaxlifetime) > 0) {
2373
            $maxlifetime = min($maxlifetime, (int)$skinmaxlifetime);
2374
            // Make sure $maxlifetime is greater than $minlifetime
2375
            $maxlifetime = max($minlifetime, $maxlifetime);
2376
        }
2377
2378
        return array($minlifetime, $maxlifetime);
2379
    }
2380
2381
    /**
2382
     * getMachineHostname
2383
     *
2384
     * This function is utilized in the formation of the URL for the
2385
     * PKCS12 credential download link.  It returns a host-specific
2386
     * URL hostname by mapping the local machine hostname (as returned
2387
     * by 'uname -n') to an InCommon metadata cilogon.org hostname
2388
     * (e.g., polo2.cilogon.org). This function contains an array
2389
     * '$hostnames' where the values are the local machine hostname and
2390
     * the keys are the *.cilogon.org hostname. Since this array is
2391
     * fairly static, I didn't see the need to read it in from a config
2392
     * file. In case the local machine hostname cannot be found in the
2393
     * $hostnames array, 'cilogon.org' is returned by default.
2394
     *
2395
     * @return string The full cilogon-specific hostname of this host.
2396
     */
2397
    public static function getMachineHostname()
2398
    {
2399
        $retval = 'cilogon.org';
2400
        $hostnames = array(
2401
            "polo1.ncsa.illinois.edu"        => "polo1.cilogon.org" ,
2402
            "poloa.ncsa.illinois.edu"        => "polo1.cilogon.org" ,
2403
            "polo2.ncsa.illinois.edu"        => "polo2.cilogon.org" ,
2404
            "polob.ncsa.illinois.edu"        => "polo2.cilogon.org" ,
2405
            "fozzie.nics.utk.edu"            => "polo3.cilogon.org" ,
2406
            "poloc.ncsa.illinois.edu"        => "test.cilogon.org" ,
2407
            "polot.ncsa.illinois.edu"        => "test.cilogon.org" ,
2408
            "polo-staging.ncsa.illinois.edu" => "test.cilogon.org" ,
2409
            "polod.ncsa.illinois.edu"        => "dev.cilogon.org" ,
2410
        );
2411
        $localhost = php_uname('n');
2412
        if (array_key_exists($localhost, $hostnames)) {
2413
            $retval = $hostnames[$localhost];
2414
        }
2415
        return $retval;
2416
    }
2417
2418
    /**
2419
     * getCompositeIdPList
2420
     *
2421
     * This function generates a list of IdPs to display in the 'Select
2422
     * An Identity Provider' box on the main CILogon page or on the
2423
     * TestIdP page. For the main CILogon page, this is a filtered list of
2424
     * IdPs based on the skin's whitelist/blacklist and the global
2425
     * blacklist file. For the TestIdP page, the list is all InCommon IdPs.
2426
     *
2427
     * @param bool $incommonidps (Optional) Show all InCommon IdPs in
2428
     *        selection list? Defaults to false, which means show only
2429
     *        whitelisted IdPs.
2430
     * @return array A two-dimensional array where the primary key is the
2431
     *         entityID and the secondary key is either 'Display_Name'
2432
     *         or 'Organization_Name'.
2433
     */
2434
    public static function getCompositeIdPList($incommonidps = false)
2435
    {
2436
        $retarray = array();
0 ignored issues
show
Unused Code introduced by
$retarray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2437
2438
        $idplist = Util::getIdpList();
2439
        if ($incommonidps) { // Get all InCommon IdPs only
2440
            $retarray = $idplist->getInCommonIdPs();
2441
        } else { // Get the whitelisted InCommon IdPs, plus maybe OAuth2 IdPs
2442
            $retarray = $idplist->getWhitelistedIdPs();
2443
2444
            // Add all OAuth2 IdPs to the list
2445
            foreach (Util::$oauth2idps as $key => $value) {
2446
                $retarray[Util::getAuthzUrl($value)]['Organization_Name'] = $value;
2447
                $retarray[Util::getAuthzUrl($value)]['Display_Name'] = $value;
2448
            }
2449
2450
            // Check to see if the skin's config.xml has a whitelist of IDPs.
2451
            // If so, go thru master IdP list and keep only those IdPs in the
2452
            // config.xml's whitelist.
2453
            $skin = Util::getSkin();
2454
            if ($skin->hasIdpWhitelist()) {
2455
                foreach ($retarray as $entityId => $names) {
2456
                    if (!$skin->idpWhitelisted($entityId)) {
2457
                        unset($retarray[$entityId]);
2458
                    }
2459
                }
2460
            }
2461
            // Next, check to see if the skin's config.xml has a blacklist of
2462
            // IdPs. If so, cull down the master IdP list removing 'bad' IdPs.
2463
            if ($skin->hasIdpBlacklist()) {
2464
                $idpblacklist = $skin->getConfigOption('idpblacklist');
2465
                foreach ($idpblacklist->idp as $blackidp) {
2466
                    unset($retarray[(string)$blackidp]);
2467
                }
2468
            }
2469
        }
2470
2471
        // Fix for CIL-174 - As suggested by Keith Hazelton, replace commas and
2472
        // hyphens with just commas.
2473
        $regex = '/(University of California)\s*[,-]\s*/';
2474
        foreach ($retarray as $entityId => $names) {
2475
            if (preg_match($regex, $names['Organization_Name'])) {
2476
                $retarray[$entityId]['Organization_Name'] =
2477
                    preg_replace($regex, '$1, ', $names['Organization_Name']);
2478
            }
2479
            if (preg_match($regex, $names['Display_Name'])) {
2480
                $retarray[$entityId]['Display_Name'] =
2481
                    preg_replace($regex, '$1, ', $names['Display_Name']);
2482
            }
2483
        }
2484
2485
        // Re-sort the retarray by Display_Name for correct alphabetization.
2486
        uasort($retarray, function ($a, $b) {
2487
            return strcasecmp(
2488
              $a['Display_Name'],
2489
              $b['Display_Name']
2490
            );
2491
        });
2492
2493
        return $retarray;
2494
    }
2495
2496
    /**
2497
     * printAttributeReleaseErrorMessage
2498
     *
2499
     * This is a convenience method called by handleGotUser to print out
2500
     * the attribute release error page to the user.
2501
     *
2502
     * @param string $ePPN
2503
     * @param string $ePTID
2504
     * @param string $firstname
2505
     * @param string $lastname
2506
     * @param string $displayname
2507
     * @param string $emailaddr
2508
     * @param string $idp
2509
     * @param string $idpname
2510
     * @param string $affiliation
2511
     * @param string $ou
2512
     * @param string $memberof
2513
     * @param string $acr
2514
     * @param string $clientparams
2515
     * @param string $redirect
2516
     * @param string $redirectform (Optional)
2517
     */
2518
    public static function printAttributeReleaseErrorMessage(
2519
        $ePPN,
2520
        $ePTID,
2521
        $firstname,
2522
        $lastname,
2523
        $displayname,
2524
        $emailaddr,
2525
        $idp,
2526
        $idpname,
2527
        $affiliation,
2528
        $ou,
0 ignored issues
show
Unused Code introduced by
The parameter $ou is not used and could be removed.

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

Loading history...
2529
        $memberof,
0 ignored issues
show
Unused Code introduced by
The parameter $memberof is not used and could be removed.

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

Loading history...
2530
        $acr,
0 ignored issues
show
Unused Code introduced by
The parameter $acr is not used and could be removed.

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

Loading history...
2531
        $clientparams,
0 ignored issues
show
Unused Code introduced by
The parameter $clientparams is not used and could be removed.

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

Loading history...
2532
        $redirect,
2533
        $redirectform = ''
2534
    ) {
2535
        $errorboxstr =
2536
        '<p>There was a problem logging on. Your identity
2537
        provider has not provided CILogon with required information.</p>
2538
        <blockquote><table cellpadding="5">';
2539
2540
        $missingattrs = '';
2541
        // Show user which attributes are missing
2542
        if ((strlen($ePPN) == 0) && (strlen($ePTID) == 0)) {
2543
            $errorboxstr .=
2544
            '<tr><th>ePTID:</th><td>MISSING</td></tr>
2545
            <tr><th>ePPN:</th><td>MISSING</td></tr>';
2546
            $missingattrs .= '%0D%0A    eduPersonPrincipalName'.
2547
                             '%0D%0A    eduPersonTargetedID ';
2548
        }
2549
        if ((strlen($firstname) == 0) && (strlen($displayname) == 0)) {
2550
            $errorboxstr .=
2551
            '<tr><th>First Name:</th><td>MISSING</td></tr>';
2552
            $missingattrs .= '%0D%0A    givenName (first name)';
2553
        }
2554
        if ((strlen($lastname) == 0) && (strlen($displayname) == 0)) {
2555
            $errorboxstr .=
2556
            '<tr><th>Last Name:</th><td>MISSING</td></tr>';
2557
            $missingattrs .= '%0D%0A    sn (last name)';
2558
        }
2559
        if ((strlen($displayname) == 0) &&
2560
            ((strlen($firstname) == 0) || (strlen($lastname) == 0))) {
2561
            $errorboxstr .=
2562
            '<tr><th>Display Name:</th><td>MISSING</td></tr>';
2563
            $missingattrs .= '%0D%0A    displayName';
2564
        }
2565
        $emailvalid = filter_var($emailaddr, FILTER_VALIDATE_EMAIL);
2566
        if ((strlen($emailaddr) == 0) || (!$emailvalid)) {
2567
            $errorboxstr .=
2568
            '<tr><th>Email Address:</th><td>' .
2569
            ((strlen($emailaddr) == 0) ? 'MISSING' : 'INVALID') .
2570
            '</td></tr>';
2571
            $missingattrs .= '%0D%0A    mail (email address)';
2572
        }
2573
        // CIL-326 - For eduGAIN IdPs, check for R&S and SIRTFI
2574
        $idplist = Util::getIdpList();
2575
        if (!$idplist->isRegisteredByInCommon($idp)) {
2576
            if (!$idplist->isREFEDSRandS($idp)) {
2577
                $errorboxstr .=
2578
                '<tr><th><a target="_blank"
2579
                href="http://refeds.org/category/research-and-scholarship">Research
2580
                and Scholarship</a>:</th><td>MISSING</td></tr>';
2581
                $missingattrs .= '%0D%0A    http://refeds.org/category/research-and-scholarship';
2582
            }
2583
            if (!$idplist->isSIRTFI($idp)) {
2584
                $errorboxstr .=
2585
                '<tr><th><a target="_blank"
2586
                href="https://refeds.org/sirtfi">SIRTFI</a>:</th><td>MISSING</td></tr>';
2587
                $missingattrs .= '%0D%0A    http://refeds.org/sirtfi';
2588
            }
2589
        }
2590
        $student = false;
2591
        $errorboxstr .= '</table></blockquote>';
2592
        if ((strlen($emailaddr) == 0) &&
2593
            (preg_match('/student@/', $affiliation))) {
2594
            $student = true;
2595
            $errorboxstr .= '<p><b>If you are a student</b>, ' .
2596
            'you may need to ask your identity provider ' .
2597
            'to release your email address.</p>';
2598
        }
2599
2600
        // Get contacts from metadata for email addresses
2601
        $shibarray = $idplist->getShibInfo($idp);
2602
        $emailmsg = '?subject=Attribute Release Problem for CILogon' .
2603
        '&[email protected]' .
2604
        '&body=Hello, I am having trouble logging on to ' .
2605
        'https://cilogon.org/ using the ' . $idpname .
2606
        ' Identity Provider (IdP) ' .
2607
        'due to the following missing attributes:%0D%0A' .
2608
        $missingattrs;
2609
        if ($student) {
2610
            $emailmsg .= '%0D%0A%0D%0ANote that my account is ' .
2611
            'marked "student" and thus my email address may need ' .
2612
            'to be released.';
2613
        }
2614
        $emailmsg .= '%0D%0A%0D%0APlease see ' .
2615
            'http://www.cilogon.org/service/addidp for more ' .
2616
            'details. Thank you for any help you can provide.';
2617
        $errorboxstr .= '<p>Contact your identity provider to ' .
2618
        'let them know you are having having a problem logging on ' .
2619
        'to CILogon.</p><blockquote><ul>';
2620
2621
        $addrfound = false;
2622
        $name = @$shibarray['Support Name'];
2623
        $addr = @$shibarray['Support Address'];
2624
        $addr = preg_replace('/^mailto:/', '', $addr);
2625
2626
        if (strlen($addr) > 0) {
2627
            $addrfound = true;
2628
            if (strlen($name) == 0) { // Use address if no name given
2629
                $name = $addr;
2630
            }
2631
            $errorboxstr .= '<li> Support Contact: ' .
2632
                $name . ' &lt;<a href="mailto:' .
2633
                $addr . $emailmsg . '">' .
2634
                $addr . '</a>&gt;</li>';
2635
        }
2636
2637
        if (!$addrfound) {
2638
            $name = @$shibarray['Technical Name'];
2639
            $addr = @$shibarray['Technical Address'];
2640
            $addr = preg_replace('/^mailto:/', '', $addr);
2641
            if (strlen($addr) > 0) {
2642
                $addrfound = true;
2643
                if (strlen($name) == 0) { // Use address if no name given
2644
                    $name = $addr;
2645
                }
2646
                $errorboxstr .= '<li> Technical Contact: ' .
2647
                    $name . ' &lt;<a href="mailto:' .
2648
                    $addr . $emailmsg . '">' .
2649
                    $addr . '</a>&gt;</li>';
2650
            }
2651
        }
2652
2653
        if (!$addrfound) {
2654
            $name = @$shibarray['Administrative Name'];
2655
            $addr = @$shibarray['Administrative Address'];
2656
            $addr = preg_replace('/^mailto:/', '', $addr);
2657
            if (strlen($addr) > 0) {
2658
                if (strlen($name) == 0) { // Use address if no name given
2659
                    $name = $addr;
2660
                }
2661
                $errorboxstr .= '<li>Administrative Contact: ' .
2662
                    $name . ' &lt;<a href="mailto:' .
2663
                    $addr . $emailmsg.'">' .
2664
                    $addr . '</a>&gt</li>';
2665
            }
2666
        }
2667
2668
        $errorboxstr .= '</ul></blockquote>
2669
2670
        <p> Alternatively, you can contact us at the email address
2671
        at the bottom of the page.</p>
2672
        ';
2673
2674
        static::printErrorBox($errorboxstr);
2675
2676
        echo '
2677
        <div>
2678
        ';
2679
2680
        static::printFormHead($redirect, 'get');
2681
        echo $redirectform , '
2682
        <input type="submit" name="submit" class="submit"
2683
        value="Proceed" />
2684
        </form>
2685
        </div>
2686
        ';
2687
    }
2688
2689
    /**
2690
     * isEduGAINAndGetCert
2691
     *
2692
     * This function checks to see if the current session IdP is an
2693
     * eduGAIN IdP (i.e., not Registered By InCommon) and the IdP does not
2694
     * have both the REFEDS R&S and SIRTFI extensions in metadata. If so,
2695
     * check to see if the transaction could be used to fetch a
2696
     * certificate. (The only time the transaction is not used to fetch
2697
     * a cert is during OIDC without the 'getcert' scope.) If all that is
2698
     * true, then return true. Otherwise return false.
2699
     *
2700
     * @param string $idp (optional) The IdP entityID. If empty, read value
2701
     *        from PHP session.
2702
     * @param string $idpname (optional) The IdP display name. If empty,
2703
     *        read value from PHP session.
2704
     * @return bool True if the current IdP is an eduGAIN IdP without
2705
     *         both REFEDS R&S and SIRTFI, AND the session could be
2706
     *         used to get a certificate.
2707
     */
2708
    public static function isEduGAINAndGetCert($idp = '', $idpname = '')
2709
    {
2710
        $retval = false; // Assume not eduGAIN IdP and getcert
2711
2712
        // If $idp or $idpname not passed in, get from current session.
2713
        if (strlen($idp) == 0) {
2714
            $idp = Util::getSessionVar('idp');
2715
        }
2716
        if (strlen($idpname) == 0) {
2717
            $idpname = Util::getSessionVar('idpname');
2718
        }
2719
2720
        // Check if this was an OIDC transaction, and if the
2721
        // 'getcert' scope was requested.
2722
        $oidcscopegetcert = false;
2723
        $oidctrans = false;
2724
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2725
        if (isset($clientparams['scope'])) {
2726
            $oidctrans = true;
2727
            if (preg_match(
2728
                '/edu\.uiuc\.ncsa\.myproxy\.getcert/',
2729
                $clientparams['scope']
2730
            )) {
2731
                $oidcscopegetcert = true;
2732
            }
2733
        }
2734
2735
        // First, make sure $idp was set and is not an OAuth2 IdP.
2736
        $idplist = Util::getIdpList();
2737
        if (((strlen($idp) > 0) &&
2738
            (strlen($idpname) > 0) &&
2739
            (!in_array($idpname, Util::$oauth2idps))) &&
2740
                (
2741
                // Next, check for eduGAIN without REFEDS R&S and SIRTFI
2742
                ((!$idplist->isRegisteredByInCommon($idp)) &&
2743
                       ((!$idplist->isREFEDSRandS($idp)) ||
2744
                        (!$idplist->isSIRTFI($idp))
2745
                       )
2746
                ) &&
2747
                // Next, check if user could get X509 cert,
2748
                // i.e., OIDC getcert scope, or a non-OIDC
2749
                // transaction such as PKCS12, JWS, or OAuth 1.0a
2750
                ($oidcscopegetcert || !$oidctrans)
2751
                )
2752
            ) {
2753
            $retval = true;
2754
        }
2755
2756
        return $retval;
2757
    }
2758
}
2759