Passed
Push — master ( b9f3f7...8e14a8 )
by Terrence
11:32
created

Content::verifyCurrentUserSession()   B

Complexity

Conditions 11
Paths 5

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 18
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 31
ccs 0
cts 25
cp 0
crap 132
rs 7.3166

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\Util;
6
use CILogon\Service\MyProxy;
7
use CILogon\Service\PortalCookie;
8
use CILogon\Service\DBService;
9
use CILogon\Service\OAuth2Provider;
10
use CILogon\Service\Loggit;
11
use Net_LDAP2_Util;
12
13
/**
14
 * Content
15
 */
16
class Content
17
{
18
    /**
19
     * printHeader
20
     *
21
     * This function should be called to print out the main HTML header
22
     * block for each web page.  This gives a consistent look to the site.
23
     * Any style changes should go in the cilogon.css file.
24
     *
25
     * @param string $title The text in the window's titlebar
26
     * @param bool $csrfcookie Set the CSRF cookie. Defaults to true.
27
     */
28
    public static function printHeader($title = '', $csrfcookie = true)
29
    {
30
        if ($csrfcookie) {
31
            $csrf = Util::getCsrf();
32
            $csrf->setTheCookie();
33
        }
34
35
        // Find the 'Powered By CILogon' image if specified by the skin
36
        $poweredbyimg = "/images/poweredbycilogon.png";
37
        $skin = Util::getSkin();
38
        $skinpoweredbyimg = (string)$skin->getConfigOption('poweredbyimg');
39
        if (
40
            (strlen($skinpoweredbyimg) > 0) &&
41
            (is_readable('/var/www/html' . $skinpoweredbyimg))
42
        ) {
43
            $poweredbyimg = $skinpoweredbyimg;
44
        }
45
46
        echo '<!doctype html>
47
<html lang="en">
48
  <head>
49
    <!-- Required meta tags -->
50
    <meta charset="utf-8" />
51
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
52
53
    <title>', $title, '</title>
54
55
    <!-- Font Awesome CSS -->
56
    <link rel="stylesheet"
57
          href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
58
          integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
59
          crossorigin="anonymous">
60
    <!-- Bootstrap CSS -->
61
    <link rel="stylesheet"
62
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
63
          integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
64
          crossorigin="anonymous" />
65
    <!-- Bootstrap-Select CSS -->
66
    <link rel="stylesheet"
67
          href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap-select.min.css" />
68
    <!-- CILogon-specific CSS -->
69
    <link rel="stylesheet" href="/include/cilogon.css" />
70
        ';
71
72
        $skin->printSkinLink();
73
74
        echo '
75
  </head>
76
77
  <body>
78
    <div class="skincilogonlogo">
79
      <a target="_blank" href="http://www.cilogon.org/faq/"><img
80
      src="', $poweredbyimg , '" alt="CILogon" title="CILogon Service" /></a>
81
    </div>
82
83
    <div class="logoheader">
84
       <h1><span>[CILogon Service]</span></h1>
85
    </div>
86
87
    <div class="mt-4 container-fluid"> <!-- Main Bootstrap Container -->
88
    ';
89
90
        static::printNoScript();
91
92
        if ((defined('BANNER_TEXT')) && (!empty(BANNER_TEXT))) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\BANNER_TEXT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
93
            echo '
94
      <div class="alert alert-warning alert-dismissible fade show" role="alert">
95
      ', BANNER_TEXT , '
96
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
97
          <span aria-hidden="true">&times;</span>
98
        </button>
99
      </div>
100
101
';
102
        }
103
    }
104
105
    /**
106
     * printFooter
107
     *
108
     * This function should be called to print out the closing HTML block
109
     * for each web page.
110
     *
111
     * @param string $footer Optional extra text to be output before the
112
     * closing footer div.
113
     */
114
    public static function printFooter($footer = '')
115
    {
116
        if (strlen($footer) > 0) {
117
            echo $footer;
118
        }
119
120
        echo '
121
    </div> <!-- Close Main Bootstrap Container -->
122
    <footer class="footer">
123
      <p>For questions about this site, please see the <a target="_blank"
124
        href="http://www.cilogon.org/faq">FAQs</a> or send email to <a
125
        href="mailto:', EMAIL_HELP, '">', EMAIL_HELP, '</a>.</p>
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\EMAIL_HELP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
126
      <p>Know <a target="_blank"
127
        href="http://ca.cilogon.org/responsibilities">your responsibilities</a>
128
        for using the CILogon Service.</p>
129
      <p>See <a target="_blank"
130
        href="http://ca.cilogon.org/acknowledgements">acknowledgements</a> of
131
        support for this site.</p>
132
    </footer>
133
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
134
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
135
            crossorigin="anonymous"></script>
136
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
137
            integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o"
138
            crossorigin="anonymous"></script>
139
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-select.min.js"></script>
140
    <script>$(document).ready(function(){ $(\'[data-toggle="popover"]\').popover(); });</script>
141
    <script>$("#collapse-gencert").on(\'shown.bs.collapse\', function(){ $("#password1").focus() });</script>
142
    <script src="/include/cilogon.js"></script>
143
  </body>
144
</html>';
145
146
        session_write_close();
147
    }
148
149
    /**
150
     * printFormHead
151
     *
152
     * This function prints out the opening <form> tag for displaying
153
     * submit buttons.  The first parameter is used for the 'action' value
154
     * of the <form>.  If omitted, getScriptDir() is called to get the
155
     * location of the current script.  This function outputs a hidden csrf
156
     * field in the form block.
157
     *
158
     * @param string $action (Optional) The value of the form's 'action'
159
     *        parameter. Defaults to getScriptDir().
160
     * @param string $method (Optional) The <form> 'method', one of 'get' or
161
     *        'post'. Defaults to 'post'.
162
     */
163
    public static function printFormHead($action = '', $method = 'post')
164
    {
165
        static $formnum = 0;
166
167
        if (strlen($action) == 0) {
168
            $action = Util::getScriptDir();
169
        }
170
171
        echo '
172
          <form action="', $action , '" method="', $method , '"
173
          autocomplete="off" id="form', sprintf("%02d", ++$formnum), '"
174
          class="needs-validation" novalidate="novalidate">
175
        ';
176
        $csrf = Util::getCsrf();
177
        echo $csrf->hiddenFormElement();
178
    }
179
180
    /**
181
     * printWAYF
182
     *
183
     * This function prints the list of IdPs in a <select> form element
184
     * which can be printed on the main login page to allow the user to
185
     * select 'Where Are You From?'.  This function checks to see if a
186
     * cookie for the 'providerId' had been set previously, so that the
187
     * last used IdP is selected in the list.
188
     *
189
     * @param bool $showremember (Optional) Show the 'Remember this
190
     *        selection' checkbox? Defaults to true.
191
     */
192
    public static function printWAYF($showremember = true)
193
    {
194
        $idps = static::getCompositeIdPList();
195
        $skin = Util::getSkin();
196
197
        $rememberhelp = 'Check this box to bypass the welcome page on ' .
198
            'subsequent visits and proceed directly to the selected ' .
199
            'identity provider. You will need to clear your browser\'s ' .
200
            'cookies to return here.';
201
        $selecthelp = '<p>
202
            CILogon facilitates secure access to CyberInfrastructure (CI).
203
            In order to use the CILogon Service, you must first select
204
            an identity provider. An identity provider (IdP) is an
205
            organization where you have an account and can log on
206
            to gain access to online services.
207
        </p>
208
        <p>
209
            If you are a faculty, staff, or student member of a university
210
            or college, please select it for your identity provider.
211
            If your school is not listed, please contact <a
212
            href=\'mailto:' . EMAIL_HELP . '\'>' . EMAIL_HELP . '</a>,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\EMAIL_HELP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
213
            and we will try to add your school in the future.
214
        </p>
215
        ';
216
217
        $googleauthz = Util::getAuthzUrl('Google');
218
        if (isset($idps[$googleauthz])) {
219
            $selecthelp .= '<p>If you have a <a target=\'_blank\'
220
            href=\'https://myaccount.google.com\'>Google</a> account,
221
            you can select it for authenticating to the CILogon Service.</p>
222
            ';
223
        }
224
        $githubauthz = Util::getAuthzUrl('GitHub');
225
        if (isset($idps[$githubauthz])) {
226
            $selecthelp .= '<p> If you have a <a target=\'_blank\'
227
            href=\'https://github.com/settings/profile\'>GitHub</a> account,
228
            you can select it for authenticating to the CILogon Service.</p>
229
            ';
230
        }
231
        $orcidauthz = Util::getAuthzUrl('ORCID');
232
        if (isset($idps[$orcidauthz])) {
233
            $selecthelp .= '<p> If you have a <a target=\'_blank\'
234
            href=\'https://orcid.org/my-orcid\'>ORCID</a> account,
235
            you can select it for authenticating to the CILogon Service.</p>
236
            ';
237
        }
238
239
        // Check if the user had previously selected an IdP from the list.
240
        // First, check the portalcookie, then the 'normal' cookie.
241
        $keepidp = '';
242
        $providerId = '';
243
        $pc = new PortalCookie();
244
        $pn = $pc->getPortalName();
245
        if (strlen($pn) > 0) {
246
            $keepidp    = $pc->get('keepidp');
247
            $providerId = $pc->get('providerId');
248
        } else {
249
            $keepidp    = Util::getCookieVar('keepidp');
250
            $providerId = Util::getCookieVar('providerId');
251
        }
252
253
        // Make sure previously selected IdP is in list of available IdPs.
254
        if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
255
            $providerId = '';
256
        }
257
258
        // If no previous providerId, get from skin, or default to Google.
259
        if (strlen($providerId) == 0) {
260
            $initialidp = (string)$skin->getConfigOption('initialidp');
261
            if ((strlen($initialidp) > 0) && (isset($idps[$initialidp]))) {
262
                $providerId = $initialidp;
263
            } else {
264
                $providerId = Util::getAuthzUrl('Google');
265
            }
266
        }
267
268
        // Check if an OIDC client selected an IdP for the transaction.
269
        // If so, verify that the IdP is in the list of available IdPs.
270
        $useselectedidp = false;
271
        $idphintlist = static::getIdphintList($idps);
272
        if (!empty($idphintlist)) {
273
            $useselectedidp = true;
274
            $providerId = $idphintlist[0];
275
            $newidps = array();
276
            // Update the IdP selection list to show just the idphintlist.
277
            foreach ($idphintlist as $value) {
278
                $newidps[$value] = $idps[$value];
279
            }
280
            $idps = $newidps;
281
            // Re-sort the $idps by Display_Name for correct alphabetization.
282
            uasort($idps, function ($a, $b) {
283
                return strcasecmp(
284
                    $a['Display_Name'],
285
                    $b['Display_Name']
286
                );
287
            });
288
        }
289
290
        echo '
291
      <div class="card text-center col-lg-6 offset-lg-3 col-md-8 offset-md-2 col-sm-10 offset-sm-1 mt-3">
292
        <h4 class="card-header">',
293
        ($useselectedidp ? 'Selected' : 'Select an'),
294
        ' Identity Provider</h4>
295
        <div class="card-body">
296
          <form action="', Util::getScriptDir(), '" method="post">
297
            <div class="form-group">
298
            <select name="providerId" id="providerId"
299
                autofocus="autofocus"
300
                class="selectpicker mw-100"
301
                data-size="20" data-width="fit"
302
                data-live-search="true"
303
                data-live-search-placeholder="Type to search"
304
                data-live-search-normalize="true"
305
                >';
306
307
308
        foreach ($idps as $entityId => $names) {
309
            echo '
310
                <option data-tokens="', $entityId , '" value="',
311
                $entityId , '"',
312
                (($entityId == $providerId) ? ' selected="selected"' : ''),
313
            '>', Util::htmlent($names['Display_Name']), '</option>';
314
        }
315
316
        echo '
317
            </select>
318
            <a href="#" tabindex="0" data-trigger="hover click"
319
            class="helpcursor"
320
            data-toggle="popover" data-html="true"
321
            title="Selecting an Identity Provider"
322
            data-content="', $selecthelp, '"><i class="fa
323
            fa-question-circle"></i></a>
324
            </div> <!-- end div form-group -->
325
            ';
326
327
        if ($showremember) {
328
            echo '
329
            <div class="form-group">
330
              <div class="form-check">
331
                <input class="form-check-input" type="checkbox"
332
                id="keepidp" name="keepidp" ',
333
                ((strlen($keepidp) > 0) ? 'checked="checked" ' : ''), ' />
334
                <label class="form-check-label"
335
                for="keepidp">Remember this selection</label>
336
                <a href="#" tabindex="0" data-trigger="hover click"
337
                class="helpcursor"
338
                data-toggle="popover" data-html="true"
339
                data-content="', $rememberhelp, '"><i class="fa
340
                fa-question-circle"></i></a>
341
              </div> <!-- end div form-check -->
342
            </div> <!-- end div form-group -->
343
            ';
344
        }
345
346
        echo Util::getCsrf()->hiddenFormElement();
347
        echo '<input type="hidden" name="previouspage" value="WAYF" />';
348
        $lobtext = static::getLogOnButtonText();
349
350
        echo '
351
            <div class="form-group">
352
              <div class="form-row align-items-center justify-content-center">
353
                <div class="col-auto">
354
                  <input type="submit" name="submit"
355
                  class="btn btn-primary submit"
356
                  value="', $lobtext , '" id="wayflogonbutton" />
357
                </div> <!-- end col-auto -->
358
        ';
359
360
        $wayfcancelbutton = Util::getSkin()->getConfigOption('wayfcancelbutton');
361
        if ((!is_null($wayfcancelbutton)) && ((int)$wayfcancelbutton == 1)) {
362
            echo '
363
                <div class="col-auto">
364
                  <input type="submit" name="submit"
365
                  class="btn btn-primary submit"
366
                  title="Cancel authentication and navigate away from this site."
367
                  value="Cancel" id="wayfcancelbutton" />
368
                </div>
369
            ';
370
        }
371
372
        echo '
373
              </div> <!-- end div form-row align-items-center -->
374
            </div> <!-- end div form-group -->
375
        ';
376
377
        $logonerror = Util::getSessionVar('logonerror');
378
        Util::unsetSessionVar('logonerror');
379
        if (strlen($logonerror) > 0) {
380
            echo '<div class="alert alert-danger" role="alert">', $logonerror, '</div>';
381
        }
382
383
        echo '
384
        <p class="privacypolicy">
385
        By selecting "', $lobtext , '", you agree to <a target="_blank"
386
        href="http://ca.cilogon.org/policy/privacy">CILogon\'s privacy
387
        policy</a>.
388
        </p>
389
390
          </form>
391
392
        </div> <!-- End card-body -->
393
      </div> <!-- End card -->
394
        ';
395
    }
396
397
    /**
398
     * printGetCertificate
399
     *
400
     * This function prints the 'Get New Certificate' box on the main page.
401
     * If the 'p12' PHP session variable is valid, it is read and a link for
402
     * the usercred.p12 file is presented to the user.
403
     */
404
    public static function printGetCertificate()
405
    {
406
        // Check if PKCS12 downloading is disabled. If so, print out message.
407
        $disabledmsg = '';
408
        $disabledbyconf = ((!defined('MYPROXY_LOGON')) || (empty(MYPROXY_LOGON)));
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYPROXY_LOGON was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
409
        $skin = Util::getSkin();
410
        $pkcs12disabled = $skin->getConfigOption('pkcs12', 'disabled');
411
        $disabledbyskin = ((!is_null($pkcs12disabled)) && ((int)$pkcs12disabled == 1));
412
        $dn = Util::getSessionVar('distinguished_name'); // Did we get user attributes for certs?
413
        $isEduGAINAndGetCert = Util::isEduGAINAndGetCert();
414
415
        if ($disabledbyconf || $disabledbyskin || (strlen($dn) == 0) || $isEduGAINAndGetCert) {
416
            if ($disabledbyconf) {
417
                $disabledmsg = 'Downloading PKCS12 certificates is disabled.';
418
            } elseif ($disabledbyskin) {
419
                $disabledmsg = $skin->getConfigOption('pkcs12', 'disabledmessage');
420
                if (!is_null($disabledmsg)) {
421
                    $disabledmsg = trim(html_entity_decode($disabledmsg));
422
                }
423
                if (strlen($disabledmsg) == 0) {
424
                    $disabledmsg = 'Downloading PKCS12 certificates is ' .
425
                        'restricted. Please try another method or log on ' .
426
                        'with a different Identity Provider.';
427
                }
428
            } elseif (strlen($dn) == 0) {
429
                $disabledmsg = 'Unable to generate a certificate. ' .
430
                    'Your identity provider has not provided CILogon ' .
431
                    'with all required information.';
432
            } elseif ($isEduGAINAndGetCert) {
433
                $disabledmsg = 'Unable to generate a certificate. ' .
434
                    'Your identity provider has not asserted support ' .
435
                    'for "Research and Scholarship" and "SIRTFI".';
436
            }
437
438
            echo '<div class="alert alert-danger" role="alert">';
439
            echo $disabledmsg;
440
            echo '</div>';
441
        } else { // PKCS12 downloading is okay
442
            $p12linktext = "Left-click this link to import the certificate " .
443
                "into your broswer / operating system. (Firefox users see " .
444
                "the FAQ.) Right-click this link and select 'Save As...' to " .
445
                "save the certificate to your desktop.";
446
            $passwordtext1 = 'Enter a password of at least 12 characters to protect your certificate.';
447
            $passwordtext2 = 'Re-enter your password for verification.';
448
449
            // Get the 'p12' session variable, which contains the time until
450
            // the "Download Certificate" link expires concatenated with the
451
            // download link (separated by a space). If either the time or
452
            // the link is blank, or the time is 0:00, then do not show the
453
            // link/time and unset the 'p12' session variable.
454
            $p12expire = '';
455
            $p12link = '';
456
            $p12linkisactive = false;
457
            $p12 = Util::getSessionVar('p12');
458
            if (preg_match('/([^\s]*)\s(.*)/', $p12, $match)) {
459
                $p12expire = $match[1];
460
                $p12link = $match[2];
461
            }
462
463
            if (
464
                (strlen($p12expire) > 0) &&
465
                (strlen($p12link) > 0) &&
466
                ($p12expire > 0) &&
467
                ($p12expire >= time())
468
            ) {
469
                $p12linkisactive = true;
470
                $expire = $p12expire - time();
471
                $minutes = floor($expire % 3600 / 60);
472
                $seconds = $expire % 60;
473
                $p12expire = 'Link Expires: ' .
474
                    sprintf("%02dm:%02ds", $minutes, $seconds);
475
                $p12link = '<a class="btn btn-primary" title="' .
476
                    $p12linktext . '" href="' . $p12link .
477
                    '">Download Your Certificate</a>';
478
            } else {
479
                $p12expire = '';
480
                $p12link = '';
481
                Util::unsetSessionVar('p12');
482
            }
483
484
            $p12lifetime = Util::getSessionVar('p12lifetime');
485
            if ((strlen($p12lifetime) == 0) || ($p12lifetime == 0)) {
486
                $p12lifetime = Util::getCookieVar('p12lifetime');
487
            }
488
            $p12multiplier = Util::getSessionVar('p12multiplier');
489
            if ((strlen($p12multiplier) == 0) || ($p12multiplier == 0)) {
490
                $p12multiplier = Util::getCookieVar('p12multiplier');
491
            }
492
493
            // Try to read the skin's intiallifetime if not yet set
494
            if ((strlen($p12lifetime) == 0) || ($p12lifetime <= 0)) {
495
                // See if the skin specified an initial value
496
                $skinlife = $skin->getConfigOption('pkcs12', 'initiallifetime', 'number');
497
                $skinmult = $skin->getConfigOption('pkcs12', 'initiallifetime', 'multiplier');
498
                if (
499
                    (!is_null($skinlife)) && (!is_null($skinmult)) &&
500
                    ((int)$skinlife > 0) && ((int)$skinmult > 0)
501
                ) {
502
                    $p12lifetime = (int)$skinlife;
503
                    $p12multiplier = (int)$skinmult;
504
                } else {
505
                    $p12lifetime = 13;      // Default to 13 months
506
                    $p12multiplier = 732;
507
                }
508
            }
509
            if ((strlen($p12multiplier) == 0) || ($p12multiplier <= 0)) {
510
                $p12multiplier = 732;   // Default to months
511
                if ($p12lifetime > 13) {
512
                    $p12lifetime = 13;
513
                }
514
            }
515
516
            // Make sure lifetime is within [minlifetime,maxlifetime]
517
            list($minlifetime, $maxlifetime) =
518
                Util::getMinMaxLifetimes('pkcs12', 9516);
519
            if (($p12lifetime * $p12multiplier) < $minlifetime) {
520
                $p12lifetime = $minlifetime;
521
                $p12multiplier = 1; // In hours
522
            } elseif (($p12lifetime * $p12multiplier) > $maxlifetime) {
523
                $p12lifetime = $maxlifetime;
524
                $p12multiplier = 1; // In hours
525
            }
526
527
            $lifetimetext = "Certificate lifetime is between $minlifetime " .
528
                "and $maxlifetime hours" .
529
                (($maxlifetime > 732) ?
530
                " ( = " . round(($maxlifetime / 732), 2) . " months)." : "."
531
                );
532
533
            $p12error = Util::getSessionVar('p12error');
534
            $expandcreatecert = (int)$skin->getConfigOption('expandcreatecert');
535
536
            static::printCollapseBegin(
537
                'gencert',
538
                'Create Password-Protected Certificate',
539
                !($p12linkisactive || (strlen($p12error) > 0) || $expandcreatecert)
540
            );
541
542
            echo '
543
          <div class="card-body col-lg-6 offset-lg-3 col-md-8 offset-md-2 col-sm-10 offset-sm-1">';
544
545
            static::printFormHead();
546
547
            if (strlen($p12error) > 0) {
548
                echo '<div class="alert alert-danger alert-dismissable fade show" role="alert">';
549
                echo $p12error;
550
                echo '
551
                      <button type="button" class="close" data-dismiss="alert"
552
                      aria-label="Close"><span aria-hidden="true">&times;</span>
553
                      </button>
554
                  </div>';
555
                Util::unsetSessionVar('p12error');
556
            }
557
558
            echo '
559
            <div class="form-group">
560
              <label for="password1">Enter Password</label>
561
              <div class="form-row">
562
                <div class="col-11">
563
                  <input type="password" name="password1" id="password1"
564
                  minlength="12" required="required"
565
                  autocomplete="new-password"
566
                  class="form-control" aria-describedby="password1help"
567
                  onkeyup="checkPassword()"/>
568
                  <div class="invalid-tooltip">
569
                    Please enter a password of at least 12 characters.
570
                  </div>
571
                </div>
572
                <div class="col">
573
                  <i id="pw1icon" class="fa fa-fw"></i>
574
                </div>
575
              </div>
576
              <div class="form-row">
577
                <div class="col-11">
578
                  <small id="password1help" class="form-text text-muted">',
579
                  $passwordtext1, '
580
                  </small>
581
                </div>
582
              </div>
583
            </div> <!-- end form-group -->
584
585
            <div class="form-group">
586
              <label for="password2">Confirm Password</label>
587
              <div class="form-row">
588
                <div class="col-11">
589
                  <input type="password" name="password2" id="password2"
590
                  minlength="12" required="required"
591
                  autocomplete="new-password"
592
                  class="form-control" aria-describedby="password2help"
593
                  onkeyup="checkPassword()"/>
594
                  <div class="invalid-tooltip">
595
                    Please ensure entered passwords match.
596
                  </div>
597
                </div>
598
                <div class="col">
599
                  <i id="pw2icon" class="fa fa-fw"></i>
600
                </div>
601
              </div>
602
              <div class="form-row">
603
                <div class="col-11">
604
                  <small id="password2help" class="form-text text-muted">',
605
                  $passwordtext2, '
606
                  </small>
607
                </div>
608
              </div>
609
            </div> <!-- end form-group -->
610
611
            <div class="form-row p12certificatelifetime">
612
              <div class="form-group col-8">
613
              <label for="p12lifetime">Lifetime</label>
614
                <input type="number" name="p12lifetime" id="p12lifetime" ',
615
                'value="', $p12lifetime, '" min="', $minlifetime, '" max="',
616
                $maxlifetime, '" class="form-control" required="required"
617
                aria-describedby="lifetime1help" />
618
                <div class="invalid-tooltip">
619
                  Please enter a valid lifetime for the certificate.
620
                </div>
621
                <small id="lifetime1help" class="form-text text-muted">',
622
                $lifetimetext, '
623
                </small>
624
              </div>
625
              <div class="form-group col-4">
626
                <label for="p12multiplier">&nbsp;</label>
627
                <select id="p12multiplier" name="p12multiplier"
628
                class="form-control">
629
                  <option value="1"',
630
                    (($p12multiplier == 1) ? ' selected="selected"' : ''),
631
                    '>hours</option>
632
                  <option value="24"',
633
                    (($p12multiplier == 24) ? ' selected="selected"' : ''),
634
                    '>days</option>
635
                  <option value="732"',
636
                    (($p12multiplier == 732) ? ' selected="selected"' : ''),
637
                    '>months</option>
638
                </select>
639
              </div>
640
            </div> <!-- end form-row -->
641
642
            <div class="form-group">
643
              <div class="form-row align-items-center">
644
                <div class="col text-center">
645
                  <input type="submit" name="submit"
646
                  class="btn btn-primary submit"
647
                  value="Get New Certificate" onclick="showHourglass(\'p12\')"/>
648
                  <div class="spinner-border"
649
                  style="width: 32px; height: 32px;"
650
                  role="status" id="p12hourglass">
651
                    <span class="sr-only">Generating...</span>
652
                  </div> <!-- spinner-border -->
653
                </div>
654
              </div>
655
            </div>
656
657
            <div class="form-group">
658
              <div class="form-row align-items-center">
659
                <div class="col text-center" id="p12value">
660
                ', $p12link, '
661
                </div>
662
              </div>
663
              <div class="form-row align-items-center">
664
                <div class="col text-center" id="p12expire">
665
                ', $p12expire, '
666
                </div>
667
              </div>
668
            </div>
669
            </form>
670
          </div> <!-- end card-body -->';
671
            static::printCollapseEnd();
672
        }
673
    }
674
675
    /**
676
     * printCertInfo
677
     *
678
     * This function prints information related to the X.509 certificate
679
     * such as DN (distinguished name) and LOA (level of assurance).
680
     */
681
    public static function printCertInfo()
682
    {
683
        $dn = Util::getSessionVar('distinguished_name');
684
        static::printCollapseBegin('certinfo', 'Certificate Information');
685
        if (strlen($dn) > 0) {
686
            // Strip off the email address from the pseudo-DN.
687
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
688
            echo '
689
              <div class="card-body">
690
                <table class="table table-striped table-sm">
691
                <tbody>
692
                  <tr>
693
                    <th>Certificate Subject:</th>
694
                    <td>', Util::htmlent($dn), '</td>
695
                  </tr>
696
                  <tr>
697
                    <th>Identity Provider:</th>
698
                    <td>', Util::getSessionVar('idp_display_name'), '</td>
699
                  </tr>
700
                  <tr>
701
                    <th><a target="_blank"
702
                      href="http://ca.cilogon.org/loa">Level of Assurance:</a></th>
703
                      <td>
704
            ';
705
706
            if (Util::getSessionVar('loa') == 'openid') {
707
                echo '<a href="http://ca.cilogon.org/policy/openid"
708
                      target="_blank">OpenID</a>';
709
            } elseif (Util::isLOASilver()) {
710
                echo '<a href="http://ca.cilogon.org/policy/silver"
711
                      target="_blank">Silver</a>';
712
            } else {
713
                echo '<a href="http://ca.cilogon.org/policy/basic"
714
                      target="_blank">Basic</a>';
715
            }
716
717
            echo '</td>
718
                  </tr>
719
                  </tbody>
720
                </table>';
721
        } else { // No DN? Show missing name(s) or email address.
722
            echo '
723
              <div class="card-body">
724
            ';
725
            static::printErrorBox(
726
                '
727
                <div class="card-text my-2">
728
                  Unable to generate a certificate. Your identity provider
729
                  has not provided CILogon with all required information.
730
                </div> <!-- end card-text -->'
731
            );
732
            $first_name   = Util::getSessionVar('first_name');
733
            $last_name    = Util::getSessionVar('last_name');
734
            $display_name = Util::getSessionVar('display_name');
735
            $email        = Util::getSessionVar('email');
736
            echo '
737
                <table class="table table-striped table-sm">
738
                <tbody>';
739
            if ((strlen($first_name) == 0) && (strlen($display_name) == 0)) {
740
                echo '
741
                  <tr>
742
                    <th class="w-50">First Name:</th>
743
                    <td>MISSING</td>
744
                  </tr>';
745
            }
746
            if ((strlen($last_name) == 0) && (strlen($display_name) == 0)) {
747
                echo '
748
                  <tr>
749
                    <th class="w-50">Last Name:</th>
750
                    <td>MISSING</td>
751
                  </tr>';
752
            }
753
            if (
754
                (strlen($display_name) == 0) &&
755
                ((strlen($first_name) == 0) || (strlen($last_name) == 0))
756
            ) {
757
                echo '
758
                  <tr>
759
                    <th class="w-50">Display Name:</th>
760
                    <td>MISSING</td>
761
                  </tr>';
762
            }
763
            $emailvalid = filter_var($email, FILTER_VALIDATE_EMAIL);
764
            if ((strlen($email) == 0) || (!$emailvalid)) {
765
                echo '
766
                  <tr>
767
                    <th class="w-50">Email Address:</th>
768
                    <td>' , ((strlen($email) == 0) ? 'MISSING' : 'INVALID') , '</td>
769
                  </tr>';
770
            }
771
            $idp     = Util::getSessionVar('idp');
772
            $idp_display_name = Util::getSessionVar('idp_display_name');
0 ignored issues
show
Unused Code introduced by
The assignment to $idp_display_name is dead and can be removed.
Loading history...
773
            if (Util::isEduGAINAndGetCert()) {
774
                $idplist = Util::getIdpList();
775
                if (!$idplist->isREFEDSRandS($idp)) {
776
                    echo '
777
                      <tr>
778
                        <th class="w-50"><a target="_blank"
779
                        href="http://refeds.org/category/research-and-scholarship">Research
780
                        and Scholarship</a>:</th>
781
                        <td>MISSING</td>
782
                      </tr>';
783
                }
784
                if (!$idplist->isSIRTFI($idp)) {
785
                    echo '
786
                      <tr>
787
                        <th class="w-50"><a target="_blank"
788
                        href="https://refeds.org/sirtfi">SIRTFI</a>:</th>
789
                        <td>MISSING</td>
790
                      </tr>';
791
                }
792
            }
793
            echo '
794
                  </tbody>
795
                </table>';
796
        }
797
        echo '
798
              </div> <!-- end card-body -->';
799
        static::printCollapseEnd();
800
    }
801
802
    /**
803
     * printUserAttributes
804
     *
805
     * This function shows the user the attributes released by their
806
     * selected IdP and saved in the PHP session.
807
     */
808
    public static function printUserAttributes()
809
    {
810
        $idplist = Util::getIdpList();
811
        $idp = Util::getSessionVar('idp');
812
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
813
814
        // Set various booleans for warning/error messages early so that we
815
        // can display a "general" warning/error icon on the card title.
816
        $warnings = array();
817
        $errors = array();
818
819
        // CIL-416 Show warning for missing ePPN
820
        if (($samlidp) && (empty(Util::getSessionVar('eppn')))) {
821
            if (empty(Util::getSessionVar('eptid'))) {
822
                $errors['no_eppn_or_eptid'] = true;
823
            } else {
824
                $warnings['no_eppn'] = true;
825
            }
826
        }
827
828
        if (empty($idp)) {
829
            $errors['no_entityID'] = true;
830
        } else {
831
            if ((!$samlidp) && (empty(Util::getSessionVar('oidc')))) {
832
                $errors['no_oidc'] = true;
833
            }
834
        }
835
836
        if ((empty(Util::getSessionVar('first_name'))) && (empty(Util::getSessionVar('display_name')))) {
837
            $errors['no_first_name'] = true;
838
        }
839
840
        if ((empty(Util::getSessionVar('last_name'))) && (empty(Util::getSessionVar('display_name')))) {
841
            $errors['no_last_name'] = true;
842
        }
843
844
        if (
845
            (empty(Util::getSessionVar('display_name'))) &&
846
            ((empty(Util::getSessionVar('first_name'))) ||
847
            (empty(Util::getSessionVar('last_name'))))
848
        ) {
849
            $errors['no_display_name'] = true;
850
        }
851
852
        $emailvalid = filter_var(Util::getSessionVar('email'), FILTER_VALIDATE_EMAIL);
853
        if ((empty(Util::getSessionVar('email'))) || (!$emailvalid)) {
854
            $errors['no_valid_email'] = true;
855
        }
856
857
        static::printCollapseBegin(
858
            'userattrs',
859
            'User Attributes ' .
860
            (
861
                (!empty($errors)) ? static::getIcon(
862
                    'fa-exclamation-circle',
863
                    'red',
864
                    'One or more missing attributes.'
865
                ) : ((@$warnings['no_eppn']) ?  static::getIcon(
866
                    'fa-exclamation-triangle',
867
                    'gold',
868
                    'Some CILogon clients (e.g., Globus) require ePPN.'
869
                ) : '')
870
            )
871
        );
872
873
        echo '
874
          <div class="card-body">
875
            <table class="table table-striped table-sm">
876
            <tbody>
877
              <tr>
878
                <th>Identity Provider (entityID):</th>
879
                <td>', $idp , '</td>
880
                <td>';
881
882
        if (@$errors['no_entityID']) {
883
            echo static::getIcon(
884
                'fa-exclamation-circle',
885
                'red',
886
                'Missing the entityID of the IdP.'
887
            );
888
        }
889
890
        echo '
891
                </td>
892
              </tr>
893
894
              <tr>
895
                <th>ePTID:</th>
896
                <td>', Util::getSessionVar('eptid'), '</td>
897
                <td>';
898
899
        if (@$errors['no_eppn_or_eptid']) {
900
            echo static::getIcon(
901
                'fa-exclamation-circle',
902
                'red',
903
                'Must have either ePPN -OR- ePTID.'
904
            );
905
        }
906
907
        echo '
908
                </td>
909
              </tr>
910
911
              <tr>
912
                <th>ePPN:</th>
913
                <td>', Util::getSessionVar('eppn'), '</td>
914
                <td>';
915
916
        if (@$errors['no_eppn_or_eptid']) {
917
            echo static::getIcon(
918
                'fa-exclamation-circle',
919
                'red',
920
                'Must have either ePPN -OR- ePTID.'
921
            );
922
        } elseif (@$warnings['no_eppn']) {
923
            echo static::getIcon(
924
                'fa-exclamation-triangle',
925
                'gold',
926
                'Some CILogon clients (e.g., Globus) require ePPN.'
927
            );
928
        }
929
930
        echo '
931
                </td>
932
              </tr>
933
934
              <tr>
935
                <th>OpenID:</th>
936
                <td>', Util::getSessionVar('oidc'), '</td>
937
                <td>';
938
939
        if (@$errors['no_oidc']) {
940
            echo static::getIcon(
941
                'fa-exclamation-circle',
942
                'red',
943
                'Missing the OpenID identifier.'
944
            );
945
        }
946
947
        echo '
948
                </td>
949
              </tr>
950
951
              <tr>
952
                <th>First Name (givenName):</th>
953
                <td>', Util::getSessionVar('first_name'), '</td>
954
                <td>';
955
956
        if (@$errors['no_first_name']) {
957
            echo static::getIcon(
958
                'fa-exclamation-circle',
959
                'red',
960
                'Must have either givenName + sn -OR- displayName.'
961
            );
962
        }
963
964
        echo '
965
                </td>
966
              </tr>
967
968
              <tr>
969
                <th>Last Name (sn):</th>
970
                <td>', Util::getSessionVar('last_name'), '</td>
971
                <td>';
972
973
        if (@$errors['no_last_name']) {
974
            echo static::getIcon(
975
                'fa-exclamation-circle',
976
                'red',
977
                'Must have either givenName + sn -OR- displayName.'
978
            );
979
        }
980
981
        echo '
982
                </td>
983
              </tr>
984
985
              <tr>
986
                <th>Display Name (displayName):</th>
987
                <td>', Util::getSessionVar('display_name'), '</td>
988
                <td>';
989
990
        if (@$errors['no_display_name']) {
991
            echo static::getIcon(
992
                'fa-exclamation-circle',
993
                'red',
994
                'Must have either displayName -OR- givenName + sn.'
995
            );
996
        }
997
998
        echo '
999
                </td>
1000
              </tr>
1001
1002
              <tr>
1003
                <th>Email Address (email):</th>
1004
                <td>', Util::getSessionVar('email'), '</td>
1005
                <td>';
1006
1007
        if (@$errors['no_valid_email']) {
1008
            echo static::getIcon(
1009
                'fa-exclamation-circle',
1010
                'red',
1011
                'Missing valid email address.'
1012
            );
1013
        }
1014
1015
        echo '
1016
                </td>
1017
              </tr>
1018
1019
              <tr>
1020
                <th>Level of Assurance (assurance):</th>
1021
                <td>', Util::getSessionVar('loa'), '</td>
1022
                <td> </td>
1023
              </tr>
1024
1025
              <tr>
1026
                <th>AuthnContextClassRef:</th>
1027
                <td>', Util::getSessionVar('acr'), '</td>
1028
                <td> </td>
1029
              </tr>
1030
1031
              <tr>
1032
                <th>Affiliation (affiliation):</th>
1033
                <td>', Util::getSessionVar('affiliation'), '</td>
1034
                <td> </td>
1035
              </tr>
1036
1037
              <tr>
1038
                <th>Entitlement (entitlement):</th>
1039
                <td>', Util::getSessionVar('entitlement'), '</td>
1040
                <td> </td>
1041
              </tr>
1042
1043
              <tr>
1044
                <th>Organizational Unit (ou):</th>
1045
                <td>', Util::getSessionVar('ou'), '</td>
1046
                <td> </td>
1047
              </tr>
1048
1049
              <tr>
1050
                <th>Member (member):</th>
1051
                <td>', Util::getSessionVar('member_of'), '</td>
1052
                <td> </td>
1053
              </tr>
1054
1055
              <tr>
1056
                <th>iTrustUIN (itrustuin):</th>
1057
                <td>', Util::getSessionVar('itrustuin'), '</td>
1058
                <td> </td>
1059
              </tr>
1060
1061
              <tr>
1062
                <th>Subject ID (subject-id):</th>
1063
                <td>', Util::getSessionVar('subject_id'), '</td>
1064
                <td> </td>
1065
              </tr>
1066
1067
              <tr>
1068
                <th>Pairwise ID (pairwise-id):</th>
1069
                <td>', Util::getSessionVar('pairwise_id'), '</td>
1070
                <td> </td>
1071
              </tr>
1072
              </tbody>
1073
            </table>
1074
          </div> <!-- end card-body -->';
1075
        static::printCollapseEnd();
1076
    }
1077
1078
    /**
1079
     * printIdPMetadata
1080
     *
1081
     * This function shows the metadata associated with the IdP saved to
1082
     * the PHP session.
1083
     */
1084
    public static function printIdPMetadata()
1085
    {
1086
        $idplist = Util::getIdpList();
1087
        $idp = Util::getSessionVar('idp');
1088
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
1089
        $shibarray = $idplist->getShibInfo($idp);
1090
1091
        // CIL-416 Check for eduGAIN IdPs without both REFEDS R&S and SIRTFI
1092
        // since these IdPs are not allowed to get certificates.
1093
        $eduGainWithoutRandSandSIRTFI = 0;
1094
        if (
1095
            ($samlidp) &&
1096
            (!$idplist->isRegisteredByInCommon($idp)) &&
1097
            ((!$idplist->isREFEDSRandS($idp)) ||
1098
             (!$idplist->isSIRTFI($idp)))
1099
        ) {
1100
            $eduGainWithoutRandSandSIRTFI = 1;
1101
        }
1102
1103
        static::printCollapseBegin(
1104
            'idpmeta',
1105
            'Identity Provider Attributes ' .
1106
            (
1107
                // CIL-416 Show warning for missing ePPN
1108
                ($eduGainWithoutRandSandSIRTFI) ?
1109
                static::getIcon(
1110
                    'fa-exclamation-triangle',
1111
                    'gold',
1112
                    'This IdP does not support both ' .
1113
                    'REFEDS R&amp;S and SIRTFI. CILogon ' .
1114
                    'functionality may be limited.'
1115
                ) : ''
1116
            )
1117
        );
1118
1119
        echo'
1120
          <div class="card-body">
1121
            <table class="table table-striped table-sm">
1122
            <tbody>
1123
              <tr>
1124
                <th>Organization Name:</th>
1125
                <td>', @$shibarray['Organization Name'] , '</td>
1126
                <td>';
1127
1128
        if (empty(@$shibarray['Organization Name'])) {
1129
            echo static::getIcon(
1130
                'fa-exclamation-circle',
1131
                'red',
1132
                'Could not find ' .
1133
                '&lt;OrganizationDisplayName&gt; in metadata.'
1134
            );
1135
        }
1136
1137
        echo '
1138
                </td>
1139
              </tr>
1140
              <tr>
1141
                <th>Home Page:</th>
1142
                <td><a target="_blank" href="', @$shibarray['Home Page'] , '">',
1143
                @$shibarray['Home Page'] , '</a></td>
1144
                <td> </td>
1145
              </tr>
1146
1147
              <tr>
1148
                <th>Support Contact:</th>';
1149
        if (
1150
            (!empty(@$shibarray['Support Name'])) ||
1151
            (!empty(@$shibarray['Support Address']))
1152
        ) {
1153
            echo '
1154
                <td>', @$shibarray['Support Name'] , ' &lt;',
1155
                        preg_replace('/^mailto:/', '', @$shibarray['Support Address']), '&gt;</td>
1156
                <td> </td>';
1157
        }
1158
        echo '
1159
              </tr>
1160
1161
        ';
1162
1163
        if ($samlidp) {
1164
            echo '
1165
              <tr>
1166
                <th>Technical Contact:</th>';
1167
            if (
1168
                (!empty(@$shibarray['Technical Name'])) ||
1169
                (!empty(@$shibarray['Technical Address']))
1170
            ) {
1171
                echo '
1172
                <td>', @$shibarray['Technical Name'] , ' &lt;',
1173
                        preg_replace('/^mailto:/', '', @$shibarray['Technical Address']), '&gt;</td>
1174
                <td> </td>';
1175
            }
1176
            echo '
1177
              </tr>
1178
1179
              <tr>
1180
                <th>Administrative Contact:</th>';
1181
            if (
1182
                (!empty(@$shibarray['Administrative Name'])) ||
1183
                (!empty(@$shibarray['Administrative Address']))
1184
            ) {
1185
                echo '
1186
                <td>', @$shibarray['Administrative Name'] , ' &lt;',
1187
                        preg_replace('/^mailto:/', '', @$shibarray['Administrative Address']), '&gt;</td>
1188
                <td> </td>';
1189
            }
1190
            echo '
1191
              </tr>
1192
1193
              <tr>
1194
                <th>Registered by InCommon:</th>
1195
                <td>', ($idplist->isRegisteredByInCommon($idp) ? 'Yes' : 'No'), '</td>
1196
                <td> </td>
1197
              </tr>
1198
1199
              <tr>
1200
                <th><a style="text-decoration:underline" target="_blank"
1201
                href="http://id.incommon.org/category/research-and-scholarship">InCommon R
1202
                &amp; S</a>:</th>
1203
                <td>', ($idplist->isInCommonRandS($idp) ? 'Yes' : 'No'), '</td>
1204
                <td> </td>
1205
              </tr>
1206
1207
              <tr>
1208
                <th><a style="text-decoration:underline" target="_blank"
1209
                href="http://refeds.org/category/research-and-scholarship">REFEDS
1210
                R &amp; S</a>:</th>
1211
                <td>', ($idplist->isREFEDSRandS($idp) ? 'Yes' : 'No'), '</td>
1212
                <td>';
1213
1214
            if (
1215
                ($eduGainWithoutRandSandSIRTFI &&
1216
                !$idplist->isREFEDSRandS($idp))
1217
            ) {
1218
                echo static::getIcon(
1219
                    'fa-exclamation-triangle',
1220
                    'gold',
1221
                    'This IdP does not support both ' .
1222
                    'REFEDS R&amp;S and SIRTFI. ' .
1223
                    'CILogon functionality may be limited.'
1224
                );
1225
            }
1226
1227
            echo '
1228
                </td>
1229
              </tr>
1230
1231
              <tr>
1232
                <th><a style="text-decoration:underline" target="_blank"
1233
                       href="https://refeds.org/sirtfi">SIRTFI</a>:</th>
1234
                <td>', ($idplist->isSIRTFI($idp) ? 'Yes' : 'No'), '</td>
1235
                <td>';
1236
1237
            if (
1238
                ($eduGainWithoutRandSandSIRTFI &&
1239
                !$idplist->isSIRTFI($idp))
1240
            ) {
1241
                echo static::getIcon(
1242
                    'fa-exclamation-triangle',
1243
                    'gold',
1244
                    'This IdP does not support both ' .
1245
                    'REFEDS R&amp;S and SIRTFI. ' .
1246
                    'CILogon functionality may be limited.'
1247
                );
1248
            }
1249
1250
            echo '
1251
                </td>
1252
              </tr>
1253
1254
              <tr>
1255
                <th><a style="text-decoration:underline" target="_blank"
1256
                href="http://id.incommon.org/assurance/bronze">InCommon Bronze</a>:</th>
1257
                <td>', ($idplist->isBronze($idp) ? 'Yes' : 'No'), '</td>
1258
                <td> </td>
1259
              </tr>
1260
1261
              <tr>
1262
                <th><a style="text-decoration:underline" target="_blank"
1263
                href="http://id.incommon.org/assurance/silver">InCommon Silver</a>:</th>
1264
                <td>', ($idplist->isSilver($idp) ? 'Yes' : 'No'), '</td>
1265
                <td> </td>
1266
              </tr>
1267
1268
              <tr>
1269
                <th>Entity ID</th>
1270
                <td><a style="text-decoration:underline" target="_blank"
1271
                href="https://met.refeds.org/met/entity/',
1272
                rawurlencode($idp),
1273
                '">', $idp, '</a></td>
1274
                <td> </td>
1275
              </tr>
1276
            ';
1277
        } // end if ($samlidp)
1278
1279
            echo '
1280
              </tbody>
1281
            </table>
1282
          </div> <!-- end card-body -->';
1283
        static::printCollapseEnd();
1284
    }
1285
1286
    /**
1287
     * getIcon
1288
     *
1289
     * This function returns the HTML for the Font Awesome icons which can
1290
     * appear inline with other information.  This is accomplished via the
1291
     * use of wrapping the image in a <span> tag.
1292
     *
1293
     * @param string $icon The Font Awesome icon to be shown.
1294
     * @param string $color The HMTL color for the icon.
1295
     * @param string $help (Optionals) The popup 'title' help text to be
1296
     *        displayed when the mouse cursor hovers over the icon.
1297
     *        Defaults to empty string.
1298
     * @return string HTML for the icon block to output.
1299
     */
1300
    public static function getIcon($icon, $color, $help = '')
1301
    {
1302
        return '<span style="color: ' . $color . ';
1303
            -webkit-text-stroke-width: 1px;
1304
            -webkit-text-stroke-color: gray;">' .
1305
            ((strlen($help) > 0) ? '<span data-trigger="hover" ' .
1306
            'data-toggle="popover" data-html="true" ' .
1307
            'data-content="' . $help . '">' : '') .
1308
            '<i class="fa nocollapse ' . $icon . '"></i>' .
1309
            ((strlen($help) > 0) ? '</span>' : '') .
1310
            '</span>';
1311
    }
1312
1313
    /**
1314
     * printCollapseBegin
1315
     *
1316
     * This function prints the preamble for a collapsible Bootstrap Card.
1317
     *
1318
     * @param string $name The name to give to the collapse elements which
1319
     *        should be unique among all collapse elements on the page.
1320
     * @param string $title The text for the card-header.
1321
     * @param bool $collapsed (optional) If true, then start with the card
1322
     *        collapsed. If false, start with the card opened.
1323
     */
1324
    public static function printCollapseBegin($name, $title, $collapsed = true)
1325
    {
1326
        echo '
1327
      <div class="card col-sm-10 offset-sm-1">
1328
        <h5 class="card-header text-center">
1329
          <a class="d-block',
1330
            ($collapsed ? ' collapsed' : ''),
1331
            '" data-toggle="collapse"
1332
            href="#collapse-', $name, '" aria-expanded="',
1333
            ($collapsed ? 'false' : "true"),
1334
            '" aria-controls="collapse-', $name, '"
1335
            id="heading-', $name, '">
1336
            <i class="fa fa-chevron-down pull-right"></i>
1337
            ', $title, '
1338
          </a>
1339
        </h5>
1340
        <div id="collapse-',$name, '" class="collapse',
1341
        ($collapsed ? '' : ' show'),
1342
        '" aria-labelledby="heading-', $name , '">';
1343
    }
1344
1345
    /**
1346
     * printCollapseEnd
1347
     *
1348
     * This function prints the closing block corresponding to the
1349
     * printCollapseBegin.
1350
     */
1351
    public static function printCollapseEnd()
1352
    {
1353
        echo '
1354
        </div> <!-- end collapse-... -->
1355
      </div> <!-- end card -->
1356
        ';
1357
    }
1358
1359
    /**
1360
     * printErrorBox
1361
     *
1362
     * This function prints out a bordered box with an error icon and any
1363
     * passed-in error HTML text.  The error icon and text are output to
1364
     * a <table> so as to keep the icon to the left of the error text.
1365
     *
1366
     * @param string $errortext HTML error text to be output
1367
     */
1368
    public static function printErrorBox($errortext)
1369
    {
1370
        echo '
1371
        <div class="alert alert-danger" role="alert">
1372
          <div class="row">
1373
            <div class="col-1 align-self-center text-center">
1374
            ', static::getIcon('fa-exclamation-circle fa-2x', 'red'),'
1375
            </div>
1376
            <div class="col">
1377
            ', $errortext , '
1378
            </div>
1379
          </div>
1380
        </div>
1381
        ';
1382
    }
1383
1384
    /**
1385
     * printNoScript
1386
     *
1387
     * This function prints the <NoScript> block which is displayed if the
1388
     * user's browser does not have JavaScript enabled.
1389
     */
1390
    public static function printNoScript()
1391
    {
1392
        echo'
1393
      <noscript>
1394
        <div class="alert alert-danger alert-dismissible" role="alert">
1395
          <span><strong>Notice: </strong> JavaScript is not enabled.
1396
          The CILogon Service requires JavaScript for functionality.
1397
          <a target="_blank" href="https://enable-javascript.com/"
1398
          class="alert-link">Please Enable JavaScript</a>.</span>
1399
        </div>
1400
      </noscript>
1401
        ';
1402
    }
1403
1404
    /**
1405
     * printLogOff
1406
     *
1407
     * This function prints the Log Of boxes at the bottom of the main page.
1408
     */
1409
    public static function printLogOff()
1410
    {
1411
        $logofftext = 'End your CILogon session and return to the ' .
1412
           'front page. Note that this will not log you out at ' .
1413
            Util::getSessionVar('idp_display_name') . '.';
1414
1415
        static::printFormHead();
1416
        echo '
1417
          <div class="form-group mt-3">
1418
            <div class="form-row align-items-center">
1419
              <div class="col text-center">
1420
              ';
1421
1422
        $logofftextbox = Util::getSkin()->getConfigOption('logofftextbox');
1423
        if ((!is_null($logofftextbox)) && ((int)$logofftextbox == 1)) {
1424
            echo '  <div class="btn btn-primary">To log off,
1425
                please quit your browser.</div>';
1426
        } else {
1427
            echo '  <input type="submit" name="submit"
1428
                class="btn btn-primary submit"
1429
                title="', $logofftext , '" value="Log Off" />';
1430
        }
1431
1432
        echo '
1433
              </div> <!-- end col-auto -->
1434
            </div> <!-- end form-row align-items-center -->
1435
          </div> <!-- end form-group -->
1436
        </form>
1437
        ';
1438
    }
1439
1440
    /**
1441
     * printGeneralErrorPage
1442
     *
1443
     * This is a convenience method called by handleGotUser to print out
1444
     * a general error page to the user.
1445
     *
1446
     * @param string $redirect The url for the <form> element
1447
     * @param string $redirectform Additional hidden input fields for the
1448
     *        <form>.
1449
     */
1450
    public static function printGeneralErrorPage($redirect, $redirectform)
1451
    {
1452
        Util::unsetAllUserSessionVars();
1453
1454
        static::printHeader('Error Logging On');
1455
        static::printCollapseBegin(
1456
            'attributeerror',
1457
            'General Error',
1458
            false
1459
        );
1460
1461
        echo '
1462
              <div class="card-body px-5">';
1463
1464
        static::printErrorBox('An error has occurred. System
1465
            administrators have been notified. This may be a temporary
1466
            error. Please try again later, or contact us at the the email
1467
            address at the bottom of the page.');
1468
1469
        static::printFormHead($redirect, 'get');
1470
1471
        echo '
1472
              <div class="card-text my-2">
1473
                <div class="form-group">
1474
                  <div class="form-row align-items-center
1475
                  justify-content-center">
1476
                    <div class="col-auto">
1477
                      ', $redirectform, '
1478
                      <input type="submit" name="submit"
1479
                      class="btn btn-primary submit form-control"
1480
                      value="Proceed" />
1481
                    </div>
1482
                  </div> <!-- end form-row align-items-center -->
1483
                </div> <!-- end form-group -->
1484
              </div> <!-- end card-text -->
1485
            </form>
1486
            </div> <!-- end card-body -->';
1487
1488
        Content::printCollapseEnd();
1489
        Content::printFooter();
1490
    }
1491
1492
    /**
1493
     * printSAMLAttributeReleaseErrorPage
1494
     *
1495
     * This is a convenience method called by handleGotUser to print out
1496
     * the attribute release error page for SAML IdPs. This can occur when
1497
     * not all attributes were released by the IdP, or when the IdP is an
1498
     * eduGAIN IdP without both R&S and SIRTFI, and the user was trying to
1499
     * get a certificate.
1500
     *
1501
     * @param string $eppn
1502
     * @param string $eptid
1503
     * @param string $first_name
1504
     * @param string $last_name
1505
     * @param string $display_name
1506
     * @param string $email
1507
     * @param string $idp
1508
     * @param string $idp_display_name
1509
     * @param string $affiliation
1510
     * @param string $ou
1511
     * @param string $member_of
1512
     * @param string $acr
1513
     * @param string $entitlement
1514
     * @param string $itrustuin
1515
     * @param string $subject_id
1516
     * @param string $pairwise_id
1517
     * @param string $clientparams
1518
     * @param string $redirect The url for the <form> element
1519
     * @param string $redirectform Additional hidden input fields for the
1520
     *        <form>.
1521
     * @param bool   $edugainandgetcert Is the IdP in eduGAIN without both
1522
     *        R&S and SIRTIF, and the user could get a certificate?
1523
     */
1524
    public static function printSAMLAttributeReleaseErrorPage(
1525
        $eppn,
1526
        $eptid,
1527
        $first_name,
1528
        $last_name,
1529
        $display_name,
1530
        $email,
1531
        $idp,
1532
        $idp_display_name,
1533
        $affiliation,
1534
        $ou,
0 ignored issues
show
Unused Code introduced by
The parameter $ou is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

1535
        /** @scrutinizer ignore-unused */ $member_of,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1539
        /** @scrutinizer ignore-unused */ $subject_id,

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

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

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

1540
        /** @scrutinizer ignore-unused */ $pairwise_id,

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

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

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

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

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

Loading history...
1542
        $redirect,
1543
        $redirectform,
1544
        $edugainandgetcert
1545
    ) {
1546
        Util::unsetAllUserSessionVars();
1547
1548
        static::printHeader('Error Logging On');
1549
        static::printCollapseBegin(
1550
            'attributeerror',
1551
            'Attribute Release Error',
1552
            false
1553
        );
1554
1555
        echo '
1556
              <div class="card-body px-5">
1557
        ';
1558
1559
        $errorboxstr = '
1560
                <div class="card-text my-2">
1561
                  There was a problem logging on. Your identity provider
1562
                  has not provided CILogon with required information.
1563
                </div> <!-- end card-text -->
1564
                <dl class="row">';
1565
1566
        $missingattrs = '';
1567
        // Show user which attributes are missing
1568
        if ((strlen($eppn) == 0) && (strlen($eptid) == 0)) {
1569
            $errorboxstr .= '
1570
                <dt class="col-sm-3">ePTID:</dt>
1571
                <dd class="col-sm-9">MISSING</dd>
1572
                <dt class="col-sm-3">ePPN:</dt>
1573
                <dd class="col-sm-9">MISSING</dd>';
1574
            $missingattrs .= '%0D%0A    eduPersonPrincipalName' .
1575
                             '%0D%0A    eduPersonTargetedID ';
1576
        }
1577
        if ((strlen($first_name) == 0) && (strlen($display_name) == 0)) {
1578
            $errorboxstr .= '
1579
                <dt class="col-sm-3">First Name:</dt>
1580
                <dd class="col-sm-9">MISSING</dd>';
1581
            $missingattrs .= '%0D%0A    givenName (first name)';
1582
        }
1583
        if ((strlen($last_name) == 0) && (strlen($display_name) == 0)) {
1584
            $errorboxstr .= '
1585
                <dt class="col-sm-3">Last Name:</dt>
1586
                <dd class="col-sm-9">MISSING</dd>';
1587
            $missingattrs .= '%0D%0A    sn (last name)';
1588
        }
1589
        if (
1590
            (strlen($display_name) == 0) &&
1591
            ((strlen($first_name) == 0) || (strlen($last_name) == 0))
1592
        ) {
1593
            $errorboxstr .= '
1594
                <dt class="col-sm-3">Display Name:</dt>
1595
                <dd class="col-sm-9">MISSING</dd>';
1596
            $missingattrs .= '%0D%0A    displayName';
1597
        }
1598
        $emailvalid = filter_var($email, FILTER_VALIDATE_EMAIL);
1599
        if ((strlen($email) == 0) || (!$emailvalid)) {
1600
            $errorboxstr .= '
1601
                <dt class="col-sm-3">Email Address:</dt>
1602
                <dd class="col-sm-9">' .
1603
            ((strlen($email) == 0) ? 'MISSING' : 'INVALID') . '</dd>';
1604
            $missingattrs .= '%0D%0A    mail (email address)';
1605
        }
1606
        // CIL-326/CIL-539 - For eduGAIN IdPs attempting to get a cert,
1607
        // print out missing R&S and SIRTFI values
1608
        $idplist = Util::getIdpList();
1609
        if ($edugainandgetcert) {
1610
            if (!$idplist->isREFEDSRandS($idp)) {
1611
                $errorboxstr .= '
1612
                    <dt class="col-sm-3"><a target="_blank"
1613
                    href="http://refeds.org/category/research-and-scholarship">Research
1614
                    and Scholarship</a>:</dt>
1615
                    <dd class="col-sm-9">MISSING</dd>';
1616
                $missingattrs .= '%0D%0A    http://refeds.org/category/research-and-scholarship';
1617
            }
1618
            if (!$idplist->isSIRTFI($idp)) {
1619
                $errorboxstr .= '
1620
                    <dt class="col-sm-3"><a target="_blank"
1621
                    href="https://refeds.org/sirtfi">SIRTFI</a>:</dt>
1622
                    <dd class="col-sm-9">MISSING</dd>';
1623
                $missingattrs .= '%0D%0A    http://refeds.org/sirtfi';
1624
            }
1625
        }
1626
        $student = false;
1627
        $errorboxstr .= '</dl>';
1628
1629
        static::printErrorBox($errorboxstr);
1630
1631
        if (
1632
            (strlen($email) == 0) &&
1633
            (preg_match('/student@/', $affiliation))
1634
        ) {
1635
            $student = true;
1636
            echo '
1637
                <div class="card-text my-2">
1638
                  <strong>If you are a student</strong>
1639
                  you may need to ask your identity provider
1640
                  to release your email address.
1641
                </div> <!-- end card-text -->
1642
            ';
1643
        }
1644
1645
        // Get contacts from metadata for email addresses
1646
        $shibarray = $idplist->getShibInfo($idp);
1647
        $emailmsg = '?subject=Attribute Release Problem for CILogon' .
1648
        '&cc=' . EMAIL_HELP .
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\EMAIL_HELP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1649
        '&body=Hello, I am having trouble logging on to ' .
1650
        'https://' . DEFAULT_HOSTNAME . '/ using the ' . $idp_display_name .
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_HOSTNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1651
        ' Identity Provider (IdP) ' .
1652
        'due to the following missing attributes:%0D%0A' .
1653
        $missingattrs;
1654
        if ($student) {
1655
            $emailmsg .= '%0D%0A%0D%0ANote that my account is ' .
1656
            'marked "student" and thus my email address may need ' .
1657
            'to be released.';
1658
        }
1659
        $emailmsg .= '%0D%0A%0D%0APlease see ' .
1660
            'http://www.cilogon.org/service/addidp for more ' .
1661
            'details. Thank you for any help you can provide.';
1662
        echo '
1663
                <div class="card-text my-2">
1664
                  Contact your identity provider to let them know you are
1665
                  having having a problem logging on to CILogon.
1666
                </div> <!-- end card-text -->
1667
                <ul>
1668
            ';
1669
1670
        $addrfound = false;
1671
        $name = @$shibarray['Support Name'];
1672
        $addr = @$shibarray['Support Address'];
1673
        $addr = preg_replace('/^mailto:/', '', $addr);
1674
1675
        if (strlen($addr) > 0) {
1676
            $addrfound = true;
1677
            if (strlen($name) == 0) { // Use address if no name given
1678
                $name = $addr;
1679
            }
1680
            echo '
1681
                  <li> Support Contact: ' ,
1682
                  $name , ' <a class="btn btn-primary" href="mailto:' ,
1683
                  $addr , $emailmsg , '">' ,
1684
                  $addr , '</a>
1685
                  </li>';
1686
        }
1687
1688
        if (!$addrfound) {
1689
            $name = @$shibarray['Technical Name'];
1690
            $addr = @$shibarray['Technical Address'];
1691
            $addr = preg_replace('/^mailto:/', '', $addr);
1692
            if (strlen($addr) > 0) {
1693
                $addrfound = true;
1694
                if (strlen($name) == 0) { // Use address if no name given
1695
                    $name = $addr;
1696
                }
1697
                echo '
1698
                      <li> Technical Contact: ' ,
1699
                      $name , ' <a class="btn btn-primary" href="mailto:' ,
1700
                      $addr , $emailmsg , '">' ,
1701
                      $addr , '</a>
1702
                      </li>';
1703
            }
1704
        }
1705
1706
        if (!$addrfound) {
1707
            $name = @$shibarray['Administrative Name'];
1708
            $addr = @$shibarray['Administrative Address'];
1709
            $addr = preg_replace('/^mailto:/', '', $addr);
1710
            if (strlen($addr) > 0) {
1711
                if (strlen($name) == 0) { // Use address if no name given
1712
                    $name = $addr;
1713
                }
1714
                echo '
1715
                      <li>Administrative Contact: ' ,
1716
                      $name , ' <a class="btn btn-primary" href="mailto:' ,
1717
                      $addr , $emailmsg , '">' ,
1718
                      $addr , '</a>
1719
                      </li>';
1720
            }
1721
        }
1722
1723
        echo '
1724
                </ul>
1725
                <div class="card-text my-2">
1726
                  Alternatively, you can contact us at the email address
1727
                  at the bottom of the page.
1728
                </div> <!-- end card-text -->
1729
            ';
1730
1731
        static::printFormHead($redirect, 'get');
1732
1733
        echo '
1734
              <div class="card-text my-2">
1735
                <div class="form-group">
1736
                  <div class="form-row align-items-center
1737
                  justify-content-center">
1738
                    <div class="col-auto">
1739
                      ', $redirectform, '
1740
                      <input type="submit" name="submit"
1741
                      class="btn btn-primary submit form-control"
1742
                      value="Proceed" />
1743
                    </div>
1744
                  </div> <!-- end form-row align-items-center -->
1745
                </div> <!-- end form-group -->
1746
              </div> <!-- end card-text -->
1747
            </form>
1748
            </div> <!-- end card-body -->';
1749
1750
        Content::printCollapseEnd();
1751
        Content::printFooter();
1752
    }
1753
1754
    /**
1755
     * printOAuth2AttributeReleaseErrorPage
1756
     *
1757
     * This function is called by handleGotUser when the IdP did not release
1758
     * all required attributes for the user. In the case of the OAuth2
1759
     * providers, this is typically due to one of first name, last name,
1760
     * and/or email address. Print out a special message for each OAuth2 IdP
1761
     * to let the user know how to fix the issue.
1762
     *
1763
     * @param string $idp_display_name The name of the OAuth2 IdP.
1764
     * @param string $redirect The url for the <form> element
1765
     * @param string $redirectform Additional hidden input fields for the
1766
     *        <form>.
1767
     *
1768
     */
1769
    public static function printOAuth2AttributeReleaseErrorPage($idp_display_name, $redirect, $redirectform)
1770
    {
1771
        Util::unsetAllUserSessionVars();
1772
        static::printHeader('Error Logging On');
1773
        static::printCollapseBegin(
1774
            'oauth2attrerror',
1775
            'Error Logging On',
1776
            false
1777
        );
1778
1779
        echo '
1780
            <div class="card-body px-5">';
1781
1782
        static::printErrorBox('There was a problem logging on.');
1783
1784
        if ($idp_display_name == 'Google') {
1785
            echo '
1786
              <div class="card-text my-2">
1787
                There was a problem logging on. It appears that you have
1788
                attempted to use Google as your identity provider, but your
1789
                name or email address was missing. To rectify this problem,
1790
                go to the <a target="_blank"
1791
                href="https://myaccount.google.com/privacy#personalinfo">Google
1792
                Account Personal Information page</a>, and enter your first
1793
                name, last name, and email address. (All other Google
1794
                account information is not required by the CILogon Service.)
1795
              </div>
1796
              <div class="card-text my-2">
1797
                After you have updated your Google account profile, click
1798
                the "Proceed" button below and attempt to log on
1799
                with your Google account again. If you have any questions,
1800
                please contact us at the email address at the bottom of the
1801
                page.
1802
              </div>';
1803
        } elseif ($idp_display_name == 'GitHub') {
1804
            echo '
1805
              <div class="card-text my-2">
1806
                There was a problem logging on. It appears that you have
1807
                attempted to use GitHub as your identity provider, but your
1808
                name or email address was missing. To rectify this problem,
1809
                go to the <a target="_blank"
1810
                href="https://github.com/settings/profile">GitHub
1811
                Public Profile page</a>, and enter your name and email
1812
                address. (All other GitHub account information is not
1813
                required by the CILogon Service.)
1814
              </div>
1815
              <div class="card-text my-2">
1816
                After you have updated your GitHub account profile, click
1817
                the "Proceed" button below and attempt to log on
1818
                with your GitHub account again. If you have any questions,
1819
                please contact us at the email address at the bottom of the
1820
                page.
1821
              </div>';
1822
        } elseif ($idp_display_name == 'ORCID') {
1823
            echo '
1824
              <div class="card-text my-2">
1825
                There was a problem logging on. It appears that you have
1826
                attempted to use ORCID as your identity provider, but your
1827
                name or email address was missing. To rectify this problem,
1828
                go to your <a target="_blank"
1829
                href="https://orcid.org/my-orcid">ORCID
1830
                Profile page</a>, enter your name and email address, and
1831
                make sure they can be viewed by Everyone.
1832
                (All other ORCID account information is not required by
1833
                the CILogon Service.)
1834
              </div>
1835
              <div class="card-text my-2">
1836
                After you have updated your ORCID account profile, click
1837
                the "Proceed" button below and attempt to log on
1838
                with your ORCID account again. If you have any questions,
1839
                please contact us at the email address at the bottom of the
1840
                page.
1841
              </div>';
1842
        }
1843
1844
        static::printFormHead($redirect, 'get');
1845
1846
        echo '
1847
              <div class="card-text my-2">
1848
                <div class="form-group">
1849
                  <div class="form-row align-items-center
1850
                  justify-content-center">
1851
                    <div class="col-auto">
1852
                      <input type="hidden" name="providerId"
1853
                      value="' ,
1854
                      Util::getAuthzUrl($idp_display_name) , '" />
1855
                      ', $redirectform, '
1856
                      <input type="submit" name="submit"
1857
                      class="btn btn-primary submit form-control"
1858
                      value="Proceed" />
1859
                    </div>
1860
                  </div> <!-- end form-row align-items-center -->
1861
                </div> <!-- end form-group -->
1862
              </div> <!-- end card-text -->
1863
            </form>
1864
            </div> <!-- end card-body -->';
1865
1866
        Content::printCollapseEnd();
1867
        Content::printFooter();
1868
    }
1869
1870
    /**
1871
     * handleLogOnButtonClicked
1872
     *
1873
     * This function is called when the user clicks the 'Log On' button
1874
     * on the IdP selection page. It checks to see if the 'Remember this
1875
     * selection' checkbox was checked and sets a cookie appropriately. It
1876
     * also sets a cookie 'providerId' so the last chosen IdP will be
1877
     * selected the next time the user visits the site. The function then
1878
     * calls the appropriate 'redirectTo...' function to send the user
1879
     * to the chosen IdP.
1880
     */
1881
    public static function handleLogOnButtonClicked()
1882
    {
1883
        // Get the list of currently available IdPs
1884
        $idps = static::getCompositeIdPList();
1885
1886
        // Set the cookie for keepidp if the checkbox was checked
1887
        $pc = new PortalCookie();
1888
        Util::setPortalOrCookieVar(
1889
            $pc,
1890
            'keepidp',
1891
            ((strlen(Util::getPostVar('keepidp')) > 0) ? 'checked' : '')
1892
        );
1893
1894
        // Get the user-chosen IdP from the posted form
1895
        $providerId = Util::getPostVar('providerId');
1896
        $providerIdValid = ((strlen($providerId) > 0) &&
1897
                            (isset($idps[$providerId])));
1898
1899
        // Set the cookie for the last chosen IdP and redirect to it if in list
1900
        Util::setPortalOrCookieVar(
1901
            $pc,
1902
            'providerId',
1903
            ($providerIdValid ? $providerId : ''),
1904
            true
1905
        );
1906
        if ($providerIdValid) {
1907
            $providerName = Util::getAuthzIdP($providerId);
1908
            if (in_array($providerName, Util::$oauth2idps)) {
1909
                // Log in with an OAuth2 IdP
1910
                static::redirectToGetOAuth2User($providerId);
1911
            } else { // Use InCommon authn
1912
                static::redirectToGetShibUser($providerId);
1913
            }
1914
        } else { // IdP not in list, or no IdP selected
1915
            Util::setSessionVar('logonerror', 'Please select a valid IdP.');
1916
            printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1916
            /** @scrutinizer ignore-call */ 
1917
            printLogonPage();
Loading history...
1917
        }
1918
    }
1919
1920
    /**
1921
     * handleNoSubmitButtonClicked
1922
     *
1923
     * This function is the 'default' case when no 'submit' button has been
1924
     * clicked, or if the submit session variable is not set. It checks
1925
     * to see if either the <forceinitialidp> option is set, or if the
1926
     * 'Remember this selection' checkbox was previously checked. If so,
1927
     * then rediret to the appropriate IdP. Otherwise, print the main
1928
     * Log On page.
1929
     */
1930
    public static function handleNoSubmitButtonClicked()
1931
    {
1932
        $providerId = '';
1933
        $keepidp = '';
1934
        $selected_idp = '';
1935
        $redirect_uri = '';
1936
        $client_id = '';
1937
        $callbackuri = Util::getSessionVar('callbackuri');
1938
        $readidpcookies = true;  // Assume config options are not set
1939
        $skin = Util::getSkin();
1940
        $forceinitialidp = (int)$skin->getConfigOption('forceinitialidp');
1941
        $initialidp = (string)$skin->getConfigOption('initialidp');
1942
1943
        // If this is a OIDC transaction, get the redirect_uri and
1944
        // client_id parameters from the session var clientparams.
1945
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1946
        if (isset($clientparams['redirect_uri'])) {
1947
            $redirect_uri = $clientparams['redirect_uri'];
1948
        }
1949
        if (isset($clientparams['client_id'])) {
1950
            $client_id = $clientparams['client_id'];
1951
        }
1952
1953
        // Use the first element of the idphint list as the selected_idp.
1954
        $idphintlist = static::getIdphintList();
1955
        if (!empty($idphintlist)) {
1956
            $selected_idp = $idphintlist[0];
1957
        }
1958
1959
        if ((strlen($redirect_uri) > 0) || (strlen($client_id) > 0)) {
1960
            // CIL-431 - If the OAuth2/OIDC $redirect_uri or $client_id is set,
1961
            // then check for a match in the BYPASS_IDP_ARRAY to see if we
1962
            // should automatically redirect to a specific IdP. Used mainly
1963
            // by campus gateways.
1964
            $bypassidp = '';
1965
            foreach (BYPASS_IDP_ARRAY as $key => $value) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\BYPASS_IDP_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1966
                if (
1967
                    (preg_match($key, $redirect_uri)) ||
1968
                    (preg_match($key, $client_id))
1969
                ) {
1970
                    $bypassidp = $value;
1971
                    break;
1972
                }
1973
            }
1974
1975
            // CIL-613 - Next, check for a match in the ALLOW_BYPASS_ARRAY.
1976
            // If found, then allow the idphint/selected_idp to be used as the
1977
            // IdP to redirect to.
1978
            if (empty($bypassidp) && (!empty($selected_idp))) {
1979
                foreach (ALLOW_BYPASS_ARRAY as $value) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\ALLOW_BYPASS_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1980
                    if (
1981
                        (preg_match($value, $redirect_uri)) ||
1982
                        (preg_match($value, $client_id))
1983
                    ) {
1984
                        $bypassidp = $selected_idp;
1985
                        break;
1986
                    }
1987
                }
1988
            }
1989
1990
            if (!empty($bypassidp)) { // Match found!
1991
                $providerId = $bypassidp;
1992
                $keepidp = 'checked';
1993
                // To skip the next code blocks, unset a few variables.
1994
                $forceinitialidp = 0;     // Skip checking this option
1995
                $selected_idp = '';       // Skip any passed-in option
1996
                $readidpcookies = false;  // Don't read in the IdP cookies
1997
            }
1998
        }
1999
2000
        // If the <forceinitialidp> option is set, use either the
2001
        // <initialidp> or the selected_idp as the providerId, and use
2002
        // <forceinitialidp> as keepIdp. Otherwise, read the cookies
2003
        // 'providerId' and 'keepidp'.
2004
        if (
2005
            ($forceinitialidp == 1) &&
2006
            ((strlen($initialidp) > 0) || (strlen($selected_idp) > 0))
2007
        ) {
2008
            // If the <allowforceinitialidp> option is set, then make sure
2009
            // the callback / redirect uri is in the portal list.
2010
            $afii = $skin->getConfigOption('portallistaction', 'allowforceinitialidp');
2011
            if (
2012
                (is_null($afii)) || // Option not set, no need to check portal list
2013
                (((int)$afii == 1) &&
2014
                  (($skin->inPortalList($redirect_uri)) ||
2015
                   ($skin->inPortalList($client_id)) ||
2016
                   ($skin->inPortalList($callbackuri))))
2017
            ) {
2018
                // 'selected_idp' takes precedence over <initialidp>
2019
                if (strlen($selected_idp) > 0) {
2020
                    $providerId = $selected_idp;
2021
                } else {
2022
                    $providerId = $initialidp;
2023
                }
2024
                $keepidp = $forceinitialidp;
2025
                $readidpcookies = false; // Don't read in the IdP cookies
2026
            }
2027
        }
2028
2029
        // <initialidp> options not set, or portal not in portal list?
2030
        // Get idp and 'Remember this selection' from cookies instead.
2031
        $pc = new PortalCookie();
2032
        $pn = $pc->getPortalName();
2033
        if ($readidpcookies) {
2034
            // Check the portalcookie first, then the 'normal' cookies
2035
            if (strlen($pn) > 0) {
2036
                $keepidp    = $pc->get('keepidp');
2037
                $providerId = $pc->get('providerId');
2038
            } else {
2039
                $keepidp    = Util::getCookieVar('keepidp');
2040
                $providerId = Util::getCookieVar('providerId');
2041
            }
2042
        }
2043
2044
        // If both 'keepidp' and 'providerId' were set (and the
2045
        // providerId is a whitelisted IdP or valid OpenID provider),
2046
        // then skip the Logon page and proceed to the appropriate
2047
        // getuser script.
2048
        if ((strlen($providerId) > 0) && (strlen($keepidp) > 0)) {
2049
            // If selected_idp was specified at the OIDC authorize endpoint,
2050
            // make sure that it matches the saved providerId. If not,
2051
            // then show the Logon page and uncheck the keepidp checkbox.
2052
            if ((strlen($selected_idp) == 0) || ($selected_idp == $providerId)) {
2053
                Util::setPortalOrCookieVar($pc, 'providerId', $providerId, true);
2054
                $providerName = Util::getAuthzIdP($providerId);
2055
                if (in_array($providerName, Util::$oauth2idps)) {
2056
                    // Log in with an OAuth2 IdP
2057
                    static::redirectToGetOAuth2User($providerId);
2058
                } elseif (Util::getIdpList()->exists($providerId)) {
2059
                    // Log in with InCommon
2060
                    static::redirectToGetShibUser($providerId);
2061
                } else { // $providerId not in whitelist
2062
                    Util::setPortalOrCookieVar($pc, 'providerId', '', true);
2063
                    printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2063
                    /** @scrutinizer ignore-call */ 
2064
                    printLogonPage();
Loading history...
2064
                }
2065
            } else { // selected_idp does not match saved providerId
2066
                Util::setPortalOrCookieVar($pc, 'keepidp', '', true);
2067
                printLogonPage();
2068
            }
2069
        } else { // One of providerId or keepidp was not set
2070
            printLogonPage();
2071
        }
2072
    }
2073
2074
    /**
2075
     * verifyCurrentUserSession
2076
     *
2077
     * This function verifies the contents of the PHP session.  It checks
2078
     * the following:
2079
     * (1) The persistent store 'user_uid', the Identity Provider 'idp',
2080
     *     the IdP Display Name 'idp_display_name', and the 'status'
2081
     *     (of getUser()) are all non-empty strings.
2082
     * (2) The 'status' (of getUser()) is even (i.e. STATUS_OK).
2083
     * (3) If $providerId is passed-in, it must match 'idp'.
2084
     * If all checks are good, then this function returns true.
2085
     *
2086
     * @param string $providerId (Optional) The user-selected Identity
2087
     *        Provider. If set, make sure $providerId matches the PHP
2088
     *        session variable 'idp'.
2089
     * @return bool True if the contents of the PHP session ar valid.
2090
     *              False otherwise.
2091
     */
2092
    public static function verifyCurrentUserSession($providerId = '')
2093
    {
2094
        $retval = false;
2095
2096
        $idp       = Util::getSessionVar('idp');
2097
        $idp_display_name   = Util::getSessionVar('idp_display_name');
2098
        $user_uid  = Util::getSessionVar('user_uid');
2099
        $status    = Util::getSessionVar('status');
2100
        $authntime = Util::getSessionVar('authntime');
2101
2102
        // CIL-410 When using the /testidp/ flow, the 'storeattributes'
2103
        // session var is set. In this case, the only attribute that
2104
        // is needed is 'idp' (entityID).
2105
        if (Util::getSessionVar('storeattributes') == '1') {
2106
            if (strlen($idp) > 0) {
2107
                $retval = true;
2108
            }
2109
        } elseif (
2110
            (strlen($user_uid) > 0) && (strlen($idp) > 0) &&
2111
            (strlen($idp_display_name) > 0) && (strlen($status) > 0) &&
2112
            (strlen($authntime) > 0) &&
2113
            (!($status & 1)) // All STATUS_OK codes are even
2114
        ) {
2115
            // If $providerId is passed in, make sure it matches the $idp
2116
            if ((strlen($providerId) == 0) || ($providerId == $idp)) {
2117
                $retval = true;
2118
                Util::getSkin()->init(); // Does the IdP need a forced skin?
2119
            }
2120
        }
2121
2122
        return $retval;
2123
    }
2124
2125
    /**
2126
     * redirectToGetShibUser
2127
     *
2128
     * This method redirects control flow to the getuser script for
2129
     * If the first parameter (a whitelisted entityId) is not specified,
2130
     * we check to see if either the providerId PHP session variable or the
2131
     * providerId cookie is set (in that order) and use one if available.
2132
     * The function then checks to see if there is a valid PHP session
2133
     * and if the providerId matches the 'idp' in the session.  If so, then
2134
     * we don't need to redirect to '/secure/getuser/' and instead we
2135
     * we display the main page.  However, if the PHP session is not valid,
2136
     * then this function redirects to the '/secure/getuser/' script so as
2137
     * to do a Shibboleth authentication via mod_shib. When the providerId
2138
     * is non-empty, the SessionInitiator will automatically go to that IdP
2139
     * (i.e. without stopping at a WAYF).  This function also sets
2140
     * several PHP session variables that are needed by the getuser script,
2141
     * including the 'responsesubmit' variable which is set as the return
2142
     * 'submit' variable in the 'getuser' script.
2143
     *
2144
     * @param string $providerId (Optional) An entityId of the
2145
     *        authenticating IdP. If not specified (or set to the empty
2146
     *        string), we check providerId PHP session variable and
2147
     *        providerId cookie (in that order) for non-empty values.
2148
     * @param string $responsesubmit (Optional) The value of the PHP session
2149
     *       'submit' variable to be set upon return from the 'getuser'
2150
     *        script.  This is utilized to control the flow of this script
2151
     *        after 'getuser'. Defaults to 'gotuser'.
2152
     * @param string $responseurl (Optional) A response url for redirection
2153
     *        after successful processing at /secure/getuser/. Defaults to
2154
     *        the current script directory.
2155
     */
2156
    public static function redirectToGetShibUser(
2157
        $providerId = '',
2158
        $responsesubmit = 'gotuser',
2159
        $responseurl = null
2160
    ) {
2161
        // If providerId not set, try the cookie value
2162
        if (strlen($providerId) == 0) {
2163
            $providerId = Util::getPortalOrCookieVar('providerId');
2164
        }
2165
2166
        // If the user has a valid 'user_uid' in the PHP session, and the
2167
        // providerId matches the 'idp' in the PHP session, then
2168
        // simply go to the main page.
2169
        if (static::verifyCurrentUserSession($providerId)) {
2170
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2170
            /** @scrutinizer ignore-call */ 
2171
            printMainPage();
Loading history...
2171
        } else { // Otherwise, redirect to the getuser script
2172
            // Set PHP session varilables needed by the getuser script
2173
            Util::setSessionVar(
2174
                'responseurl',
2175
                (is_null($responseurl) ?
2176
                    Util::getScriptDir(true) : $responseurl)
2177
            );
2178
            Util::setSessionVar('submit', 'getuser');
2179
            Util::setSessionVar('responsesubmit', $responsesubmit);
2180
            Util::getCsrf()->setCookieAndSession();
2181
2182
            // Set up the 'header' string for redirection thru mod_shib
2183
            $mhn = static::getMachineHostname($providerId);
2184
            $redirect = "Location: https://$mhn/Shibboleth.sso/Login?target=" .
2185
                urlencode("https://$mhn/secure/getuser/");
2186
2187
            if (strlen($providerId) > 0) {
2188
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
2189
                if ($providerId == 'urn:mace:incommon:nih.gov') {
2190
                    $redirect = preg_replace(
2191
                        '%/Shibboleth.sso/Login%',
2192
                        '/Shibboleth.sso/NIHLogin',
2193
                        $redirect
2194
                    );
2195
                }
2196
2197
                $redirect .= '&providerId=' . urlencode($providerId);
2198
2199
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2200
                $forceauthn = Util::getSessionVar('forceauthn');
2201
                Util::unsetSessionVar('forceauthn');
2202
                if ($forceauthn) {
2203
                    $redirect .= '&forceAuthn=true';
2204
                } elseif (strlen($forceauthn) == 0) {
2205
                    // 'forceauth' was not set to '0' in the session, so
2206
                    // check the skin's option instead.
2207
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2208
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2209
                        $redirect .= '&forceAuthn=true';
2210
                    }
2211
                }
2212
            }
2213
2214
            $log = new Loggit();
2215
            $log->info('Shibboleth Login="' . $redirect . '"');
2216
            header($redirect);
2217
            exit; // No further processing necessary
2218
        }
2219
    }
2220
2221
    /**
2222
     * redirectToGetOAuth2User
2223
     *
2224
     * This method redirects control flow to the getuser script for
2225
     * when the user logs in via OAuth 2.0. It first checks to see
2226
     * if we have a valid session. If so, we don't need to redirect and
2227
     * instead simply show the Get Certificate page. Otherwise, we start
2228
     * an OAuth 2.0 logon by composing a parameterized GET URL using
2229
     * the OAuth 2.0 endpoint.
2230
     *
2231
     * @param string $providerId (Optional) An entityId of the
2232
     *        authenticating IdP. If not specified (or set to the empty
2233
     *        string), we check providerId PHP session variable and
2234
     *        providerId cookie (in that order) for non-empty values.
2235
     * @param string $responsesubmit (Optional) The value of the PHP session
2236
     *        'submit' variable to be set upon return from the 'getuser'
2237
     *         script.  This is utilized to control the flow of this script
2238
     *         after 'getuser'. Defaults to 'gotuser'.
2239
     */
2240
    public static function redirectToGetOAuth2User(
2241
        $providerId = '',
2242
        $responsesubmit = 'gotuser'
2243
    ) {
2244
        // If providerId not set, try the cookie value
2245
        if (strlen($providerId) == 0) {
2246
            $providerId = Util::getPortalOrCookieVar('providerId');
2247
        }
2248
2249
        // If the user has a valid 'user_uid' in the PHP session, and the
2250
        // providerId matches the 'idp' in the PHP session, then
2251
        // simply go to the 'Download Certificate' button page.
2252
        if (static::verifyCurrentUserSession($providerId)) {
2253
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2253
            /** @scrutinizer ignore-call */ 
2254
            printMainPage();
Loading history...
2254
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
2255
            // Set PHP session varilables needed by the getuser script
2256
            Util::unsetSessionVar('logonerror');
2257
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
2258
            Util::setSessionVar('submit', 'getuser');
2259
            Util::setSessionVar('responsesubmit', $responsesubmit);
2260
            $csrf = Util::getCsrf();
2261
            $csrf->setCookieAndSession();
2262
            $extraparams = array();
2263
            $extraparams['state'] = $csrf->getTokenValue();
2264
2265
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2266
            $forceauthn = Util::getSessionVar('forceauthn');
2267
            Util::unsetSessionVar('forceauthn');
2268
            if ($forceauthn) {
2269
                $extraparams['approval_prompt'] = 'force';
2270
            } elseif (strlen($forceauthn) == 0) {
2271
                // 'forceauth' was not set to '0' in the session, so
2272
                // check the skin's option instead.
2273
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2274
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2275
                    $extraparams['approval_prompt'] = 'force';
2276
                }
2277
            }
2278
2279
            // Get the provider name based on the provider authz URL
2280
            $providerName = Util::getAuthzIdP($providerId);
2281
2282
            // Get the authz URL and redirect
2283
            $oauth2 = new OAuth2Provider($providerName);
2284
            if (is_null($oauth2->provider)) {
2285
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
2286
                printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2286
                /** @scrutinizer ignore-call */ 
2287
                printLogonPage();
Loading history...
2287
            } else {
2288
                $authUrl = $oauth2->provider->getAuthorizationUrl(
2289
                    array_merge(
2290
                        $oauth2->authzUrlOpts,
2291
                        $extraparams
2292
                    )
2293
                );
2294
                header('Location: ' . $authUrl);
2295
                exit; // No further processing necessary
2296
            }
2297
        }
2298
    }
2299
2300
    /**
2301
     * handleGotUser
2302
     *
2303
     * This function is called upon return from one of the getuser scripts
2304
     * which should have set the 'user_uid' and 'status' PHP session variables.
2305
     * It verifies that the status return is one of STATUS_OK (even
2306
     * values).  If not, we print an error message to the user.
2307
     */
2308
    public static function handleGotUser()
2309
    {
2310
        $log = new Loggit();
2311
        $user_uid = Util::getSessionVar('user_uid');
2312
        $status = Util::getSessionVar('status');
2313
2314
        // We must get and unset session vars BEFORE any HTML output since
2315
        // a redirect may go to another site, meaning we need to update
2316
        // the session cookie before we leave the cilogon.org domain.
2317
        //
2318
        // This bit of trickery sets local variables from the PHP session
2319
        // that was just populated, using the names in the $user_attrs array.
2320
        foreach (DBService::$user_attrs as $value) {
2321
            $$value = Util::getSessionVar($value);
2322
        }
2323
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2324
        $failureuri   = Util::getSessionVar('failureuri');
2325
        $dn           = Util::getSessionVar('distinguished_name');
2326
2327
        // CIL-410 The /testidp/ flow is indicated by the presence of the
2328
        // 'storeattributes' PHP session var. In this case, simply show
2329
        // the main testidp page with user and IdP attributes.
2330
        if (!empty(Util::getSessionVar('storeattributes'))) {
2331
            printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2331
            /** @scrutinizer ignore-call */ 
2332
            printMainPage();
Loading history...
2332
            return;
2333
        }
2334
2335
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
2336
        // If found, set 'Proceed' button redirect appropriately.
2337
        $redirect = '';
2338
        $redirectform = '';
2339
        // First, check for OIDC redirect_uri, with parameters in <form>
2340
        if (isset($clientparams['redirect_uri'])) {
2341
            $redirect = $clientparams['redirect_uri'];
2342
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
2343
                '<input type="hidden" name="error_description" value="Missing attributes" />';
2344
            if (isset($clientparams['state'])) {
2345
                $redirectform .= '<input type="hidden" name="state" value="' .
2346
                    $clientparams['state'] . '" />';
2347
            }
2348
        }
2349
2350
        // Next, check for OAuth 1.0a
2351
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
2352
            $redirect = $failureuri . "?reason=missing_attributes";
2353
        }
2354
2355
        // For the 'Create Password-Protected Certificate' flow, we now
2356
        // allow users to log in even if not all attributes are given. So
2357
        // the check for isEduGAINandGetCert happens later for X509 certs.
2358
        // Here we just check for OAuth1/OAuth2/OIDC flow and
2359
        // eduGAIN getcert restriction.
2360
        $isEduGAINAndGetCert = false;
2361
        if (
2362
            (strlen($failureuri) > 0) ||                      // OAuth 1.0a
2363
            (strlen(Util::getSessionVar('clientparams')) > 0) // OIDC
2364
        ) {
2365
            $isEduGAINAndGetCert = Util::isEduGAINAndGetCert();
2366
        }
2367
2368
        // Was this an OAuth 1.0a transaction but the distinguished_name
2369
        // could not be calculated? Set $missingparam below.
2370
        $oauth1withoutdn = ((strlen($failureuri) > 0) && (strlen($dn) == 0));
2371
2372
        // Check for various error conditions and print out appropriate page
2373
        if (
2374
            (strlen($user_uid) == 0) || // Empty user_uid
2375
            (strlen($status) == 0) ||   // Empty status
2376
            ($status & 1) ||            // Odd-numbered status = error
2377
            ($isEduGAINAndGetCert) ||   // Not allowed
2378
            ($oauth1withoutdn)          // OAuth1.0a needs DN for cert
2379
        ) {
2380
            $log->error(
2381
                'Failed to getuser' .
2382
                ($isEduGAINAndGetCert ? ' due to eduGAIN IdP restriction.' : '.')
2383
            );
2384
2385
            // Is this a SAML IdP?
2386
            $idplist = Util::getIdpList();
2387
            $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp does not exist. Did you maybe mean $idps?
Loading history...
2388
2389
            // Was there a misssing parameter?
2390
            $missingparam = (($status ==
2391
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) ||
2392
                    $oauth1withoutdn);
2393
2394
            if (($isEduGAINAndGetCert) || ($missingparam && $samlidp)) {
2395
                static::printSAMLAttributeReleaseErrorPage(
2396
                    $eppn,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eppn seems to be never defined.
Loading history...
2397
                    $eptid,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eptid seems to be never defined.
Loading history...
2398
                    $first_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $first_name seems to be never defined.
Loading history...
2399
                    $last_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last_name seems to be never defined.
Loading history...
2400
                    $display_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $display_name seems to be never defined.
Loading history...
2401
                    $email,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $email seems to be never defined.
Loading history...
2402
                    $idp,
2403
                    $idp_display_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp_display_name seems to be never defined.
Loading history...
2404
                    $affiliation,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $affiliation seems to be never defined.
Loading history...
2405
                    $ou,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ou seems to be never defined.
Loading history...
2406
                    $member_of,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $member_of seems to be never defined.
Loading history...
2407
                    $acr,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $acr seems to be never defined.
Loading history...
2408
                    $entitlement,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $entitlement seems to be never defined.
Loading history...
2409
                    $itrustuin,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $itrustuin seems to be never defined.
Loading history...
2410
                    $subject_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subject_id seems to be never defined.
Loading history...
2411
                    $pairwise_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pairwise_id seems to be never defined.
Loading history...
2412
                    $clientparams,
2413
                    $redirect,
2414
                    $redirectform,
2415
                    $isEduGAINAndGetCert
2416
                );
2417
            } elseif ($missingparam && (!$samlidp)) { // OAuth2 IdP
2418
                static::printOAuth2AttributeReleaseErrorPage(
2419
                    $idp_display_name,
2420
                    $redirect,
2421
                    $redirectform
2422
                );
2423
            } else { // General error
2424
                static::printGeneralErrorPage($redirect, $redirectform);
2425
            }
2426
        } else { // EVERYTHING IS OKAY SO FAR
2427
            // Extra security check: Once the user has successfully
2428
            // authenticated with an IdP, verify that the chosen IdP was
2429
            // actually whitelisted. If not, then set error message and show
2430
            // Select an Identity Provider page again.
2431
            Util::getSkin()->init();  // Check for forced skin
2432
            $idps = static::getCompositeIdPList();
2433
            $providerId = Util::getSessionVar('idp');
2434
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
2435
                Util::setSessionVar(
2436
                    'logonerror',
2437
                    'Invalid IdP selected. Please try again.'
2438
                );
2439
                Util::sendErrorAlert(
2440
                    'Authentication attempt using non-whitelisted IdP',
2441
                    '
2442
A user successfully authenticated with an IdP, however, the selected IdP
2443
was not in the list of whitelisted IdPs as determined by the current skin.
2444
This might indicate the user attempted to circumvent the security check
2445
in "handleGotUser()" for valid IdPs for the skin.'
2446
                );
2447
                Util::unsetCookieVar('providerId');
2448
                Util::unsetUserSessionVars();
2449
                printLogonPage();
0 ignored issues
show
Bug introduced by
The function printLogonPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2449
                /** @scrutinizer ignore-call */ 
2450
                printLogonPage();
Loading history...
2450
            } else { // Got user successfully
2451
                static::gotUserSuccess();
2452
            }
2453
        }
2454
    }
2455
2456
    /**
2457
     * gotUserSuccess
2458
     *
2459
     * This function is called after the user has been successfully
2460
     * authenticated. If the 'status' session variable is STATUS_OK
2461
     * then it checks if we have a new or changed user and logs
2462
     * that appropriately. It then continues to the MainPage.
2463
     */
2464
    public static function gotUserSuccess()
2465
    {
2466
        $log = new Loggit();
2467
        $status = Util::getSessionVar('status');
2468
2469
        // If this is the first time the user has used the CILogon Service,
2470
        // and the flow is OAuth-based, send an alert if the name contains
2471
        // any HTML entities.
2472
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2473
        $callbackuri = Util::getSessionVar('callbackuri');
2474
2475
        if (
2476
            ($status == DBService::$STATUS['STATUS_NEW_USER']) &&
2477
            ((strlen($callbackuri) > 0) ||
2478
             (isset($clientparams['code'])))
2479
        ) {
2480
            // Extra check for new users: see if any HTML entities
2481
            // are in the user name. If so, send an email alert.
2482
            $dn = Util::getSessionVar('distinguished_name');
2483
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
2484
            $htmldn = Util::htmlent($dn);
2485
            if (strcmp($dn, $htmldn) != 0) {
2486
                Util::sendErrorAlert(
2487
                    'New user DN contains HTML entities',
2488
                    "htmlentites(DN) = $htmldn\n"
2489
                );
2490
            }
2491
        }
2492
2493
        // For a new user, or if the user got new attributes, just log it.
2494
        // Then proceed to the Main Page.
2495
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
2496
            $log->info('New User.');
2497
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
2498
            $log->info('User IdP attributes changed.');
2499
        }
2500
        printMainPage();
0 ignored issues
show
Bug introduced by
The function printMainPage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2500
        /** @scrutinizer ignore-call */ 
2501
        printMainPage();
Loading history...
2501
    }
2502
2503
    /**
2504
     * generateP12
2505
     *
2506
     * This function is called when the user clicks the 'Get New
2507
     * Certificate' button. It first reads in the password fields and
2508
     * verifies that they are valid (i.e. they are long enough and match).
2509
     * Then it gets a credential from the MyProxy server and converts that
2510
     * certificate into a PKCS12 which is written to disk.  If everything
2511
     * succeeds, the temporary pkcs12 directory and lifetime is saved to
2512
     * the 'p12' PHP session variable, which is read later when the Main
2513
     * Page HTML is shown.
2514
     */
2515
    public static function generateP12()
2516
    {
2517
        $log = new Loggit();
2518
2519
        // Get the entered p12lifetime and p12multiplier and set the cookies
2520
        list($minlifetime, $maxlifetime) =
2521
            Util::getMinMaxLifetimes('pkcs12', 9516);
2522
        $p12lifetime   = Util::getPostVar('p12lifetime');
2523
        $p12multiplier = Util::getPostVar('p12multiplier');
2524
        if (strlen($p12multiplier) == 0) {
2525
            $p12multiplier = 1;  // For ECP, p12lifetime is in hours
2526
        }
2527
        $lifetime = $p12lifetime * $p12multiplier;
2528
        if ($lifetime <= 0) { // In case user entered negative number
2529
            $lifetime = $maxlifetime;
2530
            $p12lifetime = $maxlifetime;
2531
            $p12multiplier = 1;  // maxlifetime is in hours
2532
        } elseif ($lifetime < $minlifetime) {
2533
            $lifetime = $minlifetime;
2534
            $p12lifetime = $minlifetime;
2535
            $p12multiplier = 1;  // minlifetime is in hours
2536
        } elseif ($lifetime > $maxlifetime) {
2537
            $lifetime = $maxlifetime;
2538
            $p12lifetime = $maxlifetime;
2539
            $p12multiplier = 1;  // maxlifetime is in hours
2540
        }
2541
        Util::setCookieVar('p12lifetime', $p12lifetime);
2542
        Util::setCookieVar('p12multiplier', $p12multiplier);
2543
        Util::setSessionVar('p12lifetime', $p12lifetime);
2544
        Util::setSessionVar('p12multiplier', $p12multiplier);
2545
2546
        // Verify that the password is at least 12 characters long
2547
        $password1 = Util::getPostVar('password1');
2548
        $password2 = Util::getPostVar('password2');
2549
        $p12password = Util::getPostVar('p12password');  // For ECP clients
2550
        if (strlen($p12password) > 0) {
2551
            $password1 = $p12password;
2552
            $password2 = $p12password;
2553
        }
2554
        if (strlen($password1) < 12) {
2555
            Util::setSessionVar(
2556
                'p12error',
2557
                'Password must have at least 12 characters.'
2558
            );
2559
            return; // SHORT PASSWORD - NO FURTHER PROCESSING NEEDED!
2560
        }
2561
2562
        // Verify that the two password entry fields matched
2563
        if ($password1 != $password2) {
2564
            Util::setSessionVar('p12error', 'Passwords did not match.');
2565
            return; // MISMATCHED PASSWORDS - NO FURTHER PROCESSING NEEDED!
2566
        }
2567
2568
        $dn = Util::getSessionVar('distinguished_name');
2569
        if (strlen($dn) > 0) {
2570
            // Append extra info, such as 'skin', to be processed by MyProxy
2571
            $myproxyinfo = Util::getSessionVar('myproxyinfo');
2572
            if (strlen($myproxyinfo) > 0) {
2573
                $dn .= " $myproxyinfo";
2574
            }
2575
            // Attempt to fetch a credential from the MyProxy server
2576
            $cert = MyProxy::getMyProxyCredential(
2577
                $dn,
2578
                '',
2579
                MYPROXY_HOST,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYPROXY_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2580
                Util::getLOAPort(),
2581
                $lifetime,
2582
                '/var/www/config/hostcred.pem',
2583
                ''
2584
            );
2585
2586
            // The 'openssl pkcs12' command is picky in that the private
2587
            // key must appear BEFORE the public certificate. But MyProxy
2588
            // returns the private key AFTER. So swap them around.
2589
            $cert2 = '';
2590
            if (
2591
                preg_match(
2592
                    '/-----BEGIN CERTIFICATE-----([^-]+)' .
2593
                    '-----END CERTIFICATE-----[^-]*' .
2594
                    '-----BEGIN RSA PRIVATE KEY-----([^-]+)' .
2595
                    '-----END RSA PRIVATE KEY-----/',
2596
                    $cert,
2597
                    $match
2598
                )
2599
            ) {
2600
                $cert2 = "-----BEGIN RSA PRIVATE KEY-----" .
2601
                         $match[2] . "-----END RSA PRIVATE KEY-----\n" .
2602
                         "-----BEGIN CERTIFICATE-----" .
2603
                         $match[1] . "-----END CERTIFICATE-----";
2604
            }
2605
2606
            if (strlen($cert2) > 0) { // Successfully got a certificate!
2607
                // Create a temporary directory in /var/www/html/pkcs12/
2608
                $tdirparent = '/var/www/html/pkcs12/';
2609
                $polonum = '3';   // Prepend the polo? number to directory
2610
                if (preg_match('/(\d+)\./', php_uname('n'), $polomatch)) {
2611
                    $polonum = $polomatch[1];
2612
                }
2613
                $tdir = Util::tempDir($tdirparent, $polonum, 0770);
2614
                $p12dir = str_replace($tdirparent, '', $tdir);
2615
                $p12file = $tdir . '/usercred.p12';
2616
2617
                // Call the openssl pkcs12 program to convert certificate
2618
                exec('/bin/env ' .
2619
                     'RANDFILE=/tmp/.rnd ' .
2620
                     'CILOGON_PKCS12_PW=' . escapeshellarg($password1) . ' ' .
2621
                     '/usr/bin/openssl pkcs12 -export ' .
2622
                     '-passout env:CILOGON_PKCS12_PW ' .
2623
                     "-out $p12file " .
2624
                     '<<< ' . escapeshellarg($cert2));
2625
2626
                // Verify the usercred.p12 file was actually created
2627
                $size = @filesize($p12file);
2628
                if (($size !== false) && ($size > 0)) {
2629
                    $p12link = 'https://' . static::getMachineHostname() .
2630
                               '/pkcs12/' . $p12dir . '/usercred.p12';
2631
                    $p12 = (time() + 300) . " " . $p12link;
2632
                    Util::setSessionVar('p12', $p12);
2633
                    $log->info('Generated New User Certificate="' . $p12link . '"');
2634
                    //CIL-507 Special Log Message For XSEDE
2635
                    $log->info('USAGE email="' .
2636
                        Util::getSessionVar('email') . '" client="PKCS12"');
2637
                } else { // Empty or missing usercred.p12 file - shouldn't happen!
2638
                    Util::setSessionVar(
2639
                        'p12error',
2640
                        'Error creating certificate. Please try again.'
2641
                    );
2642
                    Util::deleteDir($tdir); // Remove the temporary directory
2643
                    $log->info('Error creating certificate - missing usercred.p12');
2644
                }
2645
            } else { // The myproxy-logon command failed - shouldn't happen!
2646
                Util::setSessionVar(
2647
                    'p12error',
2648
                    'Error! MyProxy unable to create certificate.'
2649
                );
2650
                $log->info('Error creating certificate - myproxy-logon failed');
2651
            }
2652
        } else { // Couldn't find the 'distinguished_name' PHP session value
2653
            Util::setSessionVar(
2654
                'p12error',
2655
                'Cannot create certificate due to missing attributes.'
2656
            );
2657
            $log->info('Error creating certificate - missing dn session variable');
2658
        }
2659
    }
2660
2661
    /**
2662
     * getLogOnButtonText
2663
     *
2664
     * This function checks the current skin to see if <logonbuttontext>
2665
     * has been configured.  If so, it returns that value.  Otherwise,
2666
     * it returns 'Log On'.
2667
     *
2668
     * @return string The text of the 'Log On' button for the WAYF, as
2669
     *         configured for the skin.  Defaults to 'Log On'.
2670
     */
2671
    public static function getLogOnButtonText()
2672
    {
2673
        $retval = 'Log On';
2674
        $lobt = Util::getSkin()->getConfigOption('logonbuttontext');
2675
        if (!is_null($lobt)) {
2676
            $retval = (string)$lobt;
2677
        }
2678
        return $retval;
2679
    }
2680
2681
    /**
2682
     * reformatDN
2683
     *
2684
     * This function takes in a certificate subject DN with the email=...
2685
     * part already removed. It checks the skin to see if <dnformat> has
2686
     * been set. If so, it reformats the DN appropriately.
2687
     *
2688
     * @param string $dn The certificate subject DN (without the email=... part)
2689
     * @return string The certificate subject DN transformed according to
2690
     *         the value of the <dnformat> skin config option.
2691
     */
2692
    public static function reformatDN($dn)
2693
    {
2694
        $newdn = $dn;
2695
        $dnformat = (string)Util::getSkin()->getConfigOption('dnformat');
2696
        if (strlen($dnformat) > 0) {
2697
            if (
2698
                ($dnformat == 'rfc2253') &&
2699
                (preg_match(
2700
                    '%/DC=(.*)/DC=(.*)/C=(.*)/O=(.*)/CN=(.*)%',
2701
                    $dn,
2702
                    $match
2703
                ))
2704
            ) {
2705
                array_shift($match);
2706
                $m = array_reverse(Net_LDAP2_Util::escape_dn_value($match));
2707
                $newdn = "CN=$m[0],O=$m[1],C=$m[2],DC=$m[3],DC=$m[4]";
2708
            }
2709
        }
2710
        return $newdn;
2711
    }
2712
2713
    /**
2714
     * getMachineHostname
2715
     *
2716
     * This function is utilized in the formation of the URL for the
2717
     * PKCS12 credential download link and for the Shibboleth Single Sign-on
2718
     * session initiator URL. It returns a host-specific URL
2719
     * hostname by mapping the local machine hostname (as returned
2720
     * by 'uname -n') to an InCommon metadata cilogon.org hostname
2721
     * (e.g., polo2.cilogon.org). This function uses the HOSTNAME_ARRAY
2722
     * where the keys are the local machine hostname and
2723
     * the values are the external facing *.cilogon.org hostname.
2724
     * In case the local machine hostname cannot be found in the
2725
     * HOSTNAME_ARRAY, DEFAULT_HOSTNAME is returned.
2726
     *
2727
     * @param string $idp The entityID of the IdP used for potential
2728
     *        special handling (e.g., for Syngenta).
2729
     * @return string The full cilogon-specific hostname of this host.
2730
     */
2731
    public static function getMachineHostname($idp = '')
2732
    {
2733
        $retval = DEFAULT_HOSTNAME;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_HOSTNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2734
        // CIL-439 For Syngenta, use just a single 'hostname' value to
2735
        // match their Active Directory configuration for CILogon's
2736
        // assertionConsumerService URL. Otherwise, map the local
2737
        // hostname to a *.cilogon.org domain name.
2738
        if ($idp != 'https://sts.windows.net/06219a4a-a835-44d5-afaf-3926343bfb89/') {
2739
            $localhost = php_uname('n');
2740
            if (array_key_exists($localhost, HOSTNAME_ARRAY)) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\HOSTNAME_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2741
                $retval = HOSTNAME_ARRAY[$localhost];
2742
            }
2743
        }
2744
        return $retval;
2745
    }
2746
2747
    /**
2748
     * getCompositeIdPList
2749
     *
2750
     * This function generates a list of IdPs to display in the 'Select
2751
     * An Identity Provider' box on the main CILogon page or on the
2752
     * TestIdP page. For the main CILogon page, this is a filtered list of
2753
     * IdPs based on the skin's whitelist/blacklist and the global
2754
     * blacklist file. For the TestIdP page, the list is all InCommon IdPs.
2755
     *
2756
     * @return array A two-dimensional array where the primary key is the
2757
     *         entityID and the secondary key is either 'Display_Name'
2758
     *         or 'Organization_Name'.
2759
     */
2760
    public static function getCompositeIdPList()
2761
    {
2762
        $retarray = array();
2763
2764
        $idplist = Util::getIdpList();
2765
        $skin = Util::getSkin();
2766
2767
        // Check if the skin's config.xml has set the
2768
        // 'registeredbyincommonidps' option, which restricts the SAML-
2769
        // based IdPs to those with the <Registered_By_InCommon> tag.
2770
        // Otherwise, just get the SAML-based IdPs that have the
2771
        // <Whitelisted> tag. Note that the skin's <idpwhitelist>
2772
        // is still consulted in either case (below).
2773
        $registeredbyincommonidps = $skin->getConfigOption('registeredbyincommonidps');
2774
        if (
2775
            (!is_null($registeredbyincommonidps)) &&
2776
            ((int)$registeredbyincommonidps == 1)
2777
        ) {
2778
            $retarray = $idplist->getRegisteredByInCommonIdPs();
2779
        } else {
2780
            $retarray = $idplist->getWhitelistedIdPs();
2781
        }
2782
2783
        // Add all OAuth2 IdPs to the list
2784
        foreach (Util::$oauth2idps as $value) {
2785
            // CIL-617 Show OAuth2 IdPs only if client_id is configured
2786
            $client_id = constant(strtoupper($value) . '_OAUTH2_CLIENT_ID');
2787
            if (!empty($client_id)) {
2788
                $retarray[Util::getAuthzUrl($value)]['Organization_Name'] =
2789
                    $value;
2790
                $retarray[Util::getAuthzUrl($value)]['Display_Name'] =
2791
                    $value;
2792
            }
2793
        }
2794
2795
        // Check to see if the skin's config.xml has a whitelist of IDPs.
2796
        // If so, go thru master IdP list and keep only those IdPs in the
2797
        // config.xml's whitelist.
2798
        if ($skin->hasIdpWhitelist()) {
2799
            foreach ($retarray as $entityId => $names) {
2800
                if (!$skin->idpWhitelisted($entityId)) {
2801
                    unset($retarray[$entityId]);
2802
                }
2803
            }
2804
        }
2805
        // Next, check to see if the skin's config.xml has a blacklist of
2806
        // IdPs. If so, cull down the master IdP list removing 'bad' IdPs.
2807
        if ($skin->hasIdpBlacklist()) {
2808
            $idpblacklist = $skin->getConfigOption('idpblacklist');
2809
            foreach ($idpblacklist->idp as $blackidp) {
2810
                unset($retarray[(string)$blackidp]);
2811
            }
2812
        }
2813
2814
        // Fix for CIL-174 - As suggested by Keith Hazelton, replace commas and
2815
        // hyphens with just commas.
2816
        $regex = '/(University of California)\s*[,-]\s*/';
2817
        foreach ($retarray as $entityId => $names) {
2818
            if (preg_match($regex, $names['Organization_Name'])) {
2819
                $retarray[$entityId]['Organization_Name'] =
2820
                    preg_replace($regex, '$1, ', $names['Organization_Name']);
2821
            }
2822
            if (preg_match($regex, $names['Display_Name'])) {
2823
                $retarray[$entityId]['Display_Name'] =
2824
                    preg_replace($regex, '$1, ', $names['Display_Name']);
2825
            }
2826
        }
2827
2828
        // Re-sort the retarray by Display_Name for correct alphabetization.
2829
        uasort($retarray, function ($a, $b) {
2830
            return strcasecmp(
2831
                $a['Display_Name'],
2832
                $b['Display_Name']
2833
            );
2834
        });
2835
2836
        return $retarray;
2837
    }
2838
2839
    /**
2840
     * getIdphintList
2841
     *
2842
     * This function adds support for AARC-G049 "IdP Hinting". It
2843
     * searches both the GET query parameters and the OIDC client
2844
     * parameters passed to the 'authorize' endpoint for a parameter
2845
     * named either 'selected_idp' or 'idphint'. This parameter can be
2846
     * a single entityId or a comma-separated list of entityIds.
2847
     * The entries in the list are processed to remove any 'chained'
2848
     * idphints and also to transform OIDC 'issuer' values into
2849
     * CILogon-specific 'entityIds' as used in the 'Select an IdP'
2850
     * list. Any idps which are not in the current skin's 'Select
2851
     * an IdP' list are removed. The resulting processed list of
2852
     * entityIds is returned, which may be an empty array.
2853
     *
2854
     * @param array $idps (Optional) A list of valid (i.e., whitelisted) IdPs.
2855
     *        If this list is empty, then use the current skin's IdP list.
2856
     * @return array A list of entityIds / OIDC provider URLs extracted from
2857
     *         a passed-in parameter 'selected_idp' or 'idphint'. This array
2858
     *         may be empty if no such parameter was found, or if the
2859
     *         entityIds in the list were not valid.
2860
     */
2861
    public static function getIdphintList($idps = [])
2862
    {
2863
        // Check for either 'selected_idp' or 'idphint' parameter that was
2864
        // passed in via a query parameter, either for an OAuth transaction
2865
        // or just 'normally'. Note that if both 'selected_idp' and
2866
        // 'idphint' were passed, 'idphint' takes priority.
2867
2868
        $hintarray = array();
2869
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2870
2871
        $hintstr = '';
2872
        if (!empty(@$clientparams['idphint'])) {
2873
            $hintstr = $clientparams['idphint'];
2874
        } elseif (!empty(Util::getGetVar('idphint'))) {
2875
            $hintstr = Util::getGetVar('idphint');
2876
        } elseif (!empty(@$clientparams['selected_idp'])) {
2877
            $hintstr = $clientparams['selected_idp'];
2878
        } elseif (!empty(Util::getGetVar('selected_idp'))) {
2879
            $hintstr = Util::getGetVar('selected_idp');
2880
        }
2881
2882
        if (!empty($hintstr)) {
2883
            // Split on comma to account for multiple idps
2884
            $hintarray = explode(',', $hintstr);
2885
2886
            // Process the list of IdPs to transform them appropriately.
2887
            foreach ($hintarray as &$value) {
2888
                // Check for 'chained' idp hints, and remove the GET params.
2889
                if (preg_match('%([^\?]*)\?%', $value, $matches)) {
2890
                    $value = $matches[1];
2891
                }
2892
                // Also, check for OIDC issuers and transform them into
2893
                // CILogon-specific values used in the 'Select an IdP' list.
2894
                if (preg_match('%https://accounts.google.com%', $value)) {
2895
                    $value = 'https://accounts.google.com/o/oauth2/auth';
2896
                } elseif (preg_match('%https://github.com%', $value)) {
2897
                    $value = 'https://github.com/login/oauth/authorize';
2898
                } elseif (preg_match('%https://orcid.org%', $value)) {
2899
                    $value = 'https://orcid.org/oauth/authorize';
2900
                }
2901
            }
2902
            unset($value); // Break the reference with the last element.
2903
2904
            // Remove any non-whitelisted IdPs from the hintarray.
2905
            if (empty($idps)) {
2906
                $idps = static::getCompositeIdPList();
2907
            }
2908
            foreach ($hintarray as $value) {
2909
                if (!isset($idps[$value])) {
2910
                    if (($key = array_search($value, $hintarray)) !== false) {
2911
                        unset($hintarray[$key]);
2912
                    }
2913
                }
2914
            }
2915
        }
2916
        return $hintarray;
2917
    }
2918
}
2919