Passed
Push — master ( e2f802...d5face )
by Terrence
11:55
created

Content::getSerialStringFromDN()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 14
ccs 0
cts 9
cp 0
crap 12
rs 10
c 1
b 0
f 0
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
414
        if ($disabledbyconf || $disabledbyskin || (strlen($dn) == 0)) {
415
            if ($disabledbyconf) {
416
                $disabledmsg = 'Downloading PKCS12 certificates is disabled.';
417
            } elseif ($disabledbyskin) {
418
                $disabledmsg = $skin->getConfigOption('pkcs12', 'disabledmessage');
419
                if (!is_null($disabledmsg)) {
420
                    $disabledmsg = trim(html_entity_decode($disabledmsg));
421
                }
422
                if (strlen($disabledmsg) == 0) {
423
                    $disabledmsg = 'Downloading PKCS12 certificates is ' .
424
                        'restricted. Please try another method or log on ' .
425
                        'with a different Identity Provider.';
426
                }
427
            } elseif (strlen($dn) == 0) {
428
                $disabledmsg = 'Unable to generate a certificate. ' .
429
                    'Your identity provider has not provided CILogon ' .
430
                    'with all required information.';
431
            }
432
433
            echo '<div class="alert alert-danger" role="alert">';
434
            echo $disabledmsg;
435
            echo '</div>';
436
        } else { // PKCS12 downloading is okay
437
            $p12linktext = "Left-click this link to import the certificate " .
438
                "into your broswer / operating system. (Firefox users see " .
439
                "the FAQ.) Right-click this link and select 'Save As...' to " .
440
                "save the certificate to your desktop.";
441
            $passwordtext1 = 'Enter a password of at least 12 characters to protect your certificate.';
442
            $passwordtext2 = 'Re-enter your password for verification.';
443
444
            // Get the 'p12' session variable, which contains the time until
445
            // the "Download Certificate" link expires concatenated with the
446
            // download link (separated by a space). If either the time or
447
            // the link is blank, or the time is 0:00, then do not show the
448
            // link/time and unset the 'p12' session variable.
449
            $p12expire = '';
450
            $p12link = '';
451
            $p12linkisactive = false;
452
            $p12 = Util::getSessionVar('p12');
453
            if (preg_match('/([^\s]*)\s(.*)/', $p12, $match)) {
454
                $p12expire = $match[1];
455
                $p12link = $match[2];
456
            }
457
458
            if (
459
                (strlen($p12expire) > 0) &&
460
                (strlen($p12link) > 0) &&
461
                ($p12expire > 0) &&
462
                ($p12expire >= time())
463
            ) {
464
                $p12linkisactive = true;
465
                $expire = $p12expire - time();
466
                $minutes = floor($expire % 3600 / 60);
467
                $seconds = $expire % 60;
468
                $p12expire = 'Link Expires: ' .
469
                    sprintf("%02dm:%02ds", $minutes, $seconds);
470
                $p12link = '<a class="btn btn-primary" title="' .
471
                    $p12linktext . '" href="' . $p12link .
472
                    '">Download Your Certificate</a>';
473
            } else {
474
                $p12expire = '';
475
                $p12link = '';
476
                Util::unsetSessionVar('p12');
477
            }
478
479
            $p12lifetime = Util::getSessionVar('p12lifetime');
480
            if ((strlen($p12lifetime) == 0) || ($p12lifetime == 0)) {
481
                $p12lifetime = Util::getCookieVar('p12lifetime');
482
            }
483
            $p12multiplier = Util::getSessionVar('p12multiplier');
484
            if ((strlen($p12multiplier) == 0) || ($p12multiplier == 0)) {
485
                $p12multiplier = Util::getCookieVar('p12multiplier');
486
            }
487
488
            // Try to read the skin's intiallifetime if not yet set
489
            if ((strlen($p12lifetime) == 0) || ($p12lifetime <= 0)) {
490
                // See if the skin specified an initial value
491
                $skinlife = $skin->getConfigOption('pkcs12', 'initiallifetime', 'number');
492
                $skinmult = $skin->getConfigOption('pkcs12', 'initiallifetime', 'multiplier');
493
                if (
494
                    (!is_null($skinlife)) && (!is_null($skinmult)) &&
495
                    ((int)$skinlife > 0) && ((int)$skinmult > 0)
496
                ) {
497
                    $p12lifetime = (int)$skinlife;
498
                    $p12multiplier = (int)$skinmult;
499
                } else {
500
                    $p12lifetime = 13;      // Default to 13 months
501
                    $p12multiplier = 732;
502
                }
503
            }
504
            if ((strlen($p12multiplier) == 0) || ($p12multiplier <= 0)) {
505
                $p12multiplier = 732;   // Default to months
506
                if ($p12lifetime > 13) {
507
                    $p12lifetime = 13;
508
                }
509
            }
510
511
            // Make sure lifetime is within [minlifetime,maxlifetime]
512
            list($minlifetime, $maxlifetime) =
513
                Util::getMinMaxLifetimes('pkcs12', 9516);
514
            if (($p12lifetime * $p12multiplier) < $minlifetime) {
515
                $p12lifetime = $minlifetime;
516
                $p12multiplier = 1; // In hours
517
            } elseif (($p12lifetime * $p12multiplier) > $maxlifetime) {
518
                $p12lifetime = $maxlifetime;
519
                $p12multiplier = 1; // In hours
520
            }
521
522
            $lifetimetext = "Certificate lifetime is between $minlifetime " .
523
                "and $maxlifetime hours" .
524
                (($maxlifetime > 732) ?
525
                " ( = " . round(($maxlifetime / 732), 2) . " months)." : "."
526
                );
527
528
            $p12error = Util::getSessionVar('p12error');
529
            $expandcreatecert = (int)$skin->getConfigOption('expandcreatecert');
530
531
            static::printCollapseBegin(
532
                'gencert',
533
                'Create Password-Protected Certificate',
534
                !($p12linkisactive || (strlen($p12error) > 0) || $expandcreatecert)
535
            );
536
537
            echo '
538
          <div class="card-body col-lg-6 offset-lg-3 col-md-8 offset-md-2 col-sm-10 offset-sm-1">';
539
540
            static::printFormHead();
541
542
            if (strlen($p12error) > 0) {
543
                echo '<div class="alert alert-danger alert-dismissable fade show" role="alert">';
544
                echo $p12error;
545
                echo '
546
                      <button type="button" class="close" data-dismiss="alert"
547
                      aria-label="Close"><span aria-hidden="true">&times;</span>
548
                      </button>
549
                  </div>';
550
                Util::unsetSessionVar('p12error');
551
            }
552
553
            echo '
554
            <div class="form-group">
555
              <label for="password1">Enter Password</label>
556
              <div class="form-row">
557
                <div class="col-11">
558
                  <input type="password" name="password1" id="password1"
559
                  minlength="12" required="required"
560
                  autocomplete="new-password"
561
                  class="form-control" aria-describedby="password1help"
562
                  onkeyup="checkPassword()"/>
563
                  <div class="invalid-tooltip">
564
                    Please enter a password of at least 12 characters.
565
                  </div>
566
                </div>
567
                <div class="col">
568
                  <i id="pw1icon" class="fa fa-fw"></i>
569
                </div>
570
              </div>
571
              <div class="form-row">
572
                <div class="col-11">
573
                  <small id="password1help" class="form-text text-muted">',
574
                  $passwordtext1, '
575
                  </small>
576
                </div>
577
              </div>
578
            </div> <!-- end form-group -->
579
580
            <div class="form-group">
581
              <label for="password2">Confirm Password</label>
582
              <div class="form-row">
583
                <div class="col-11">
584
                  <input type="password" name="password2" id="password2"
585
                  minlength="12" required="required"
586
                  autocomplete="new-password"
587
                  class="form-control" aria-describedby="password2help"
588
                  onkeyup="checkPassword()"/>
589
                  <div class="invalid-tooltip">
590
                    Please ensure entered passwords match.
591
                  </div>
592
                </div>
593
                <div class="col">
594
                  <i id="pw2icon" class="fa fa-fw"></i>
595
                </div>
596
              </div>
597
              <div class="form-row">
598
                <div class="col-11">
599
                  <small id="password2help" class="form-text text-muted">',
600
                  $passwordtext2, '
601
                  </small>
602
                </div>
603
              </div>
604
            </div> <!-- end form-group -->
605
606
            <div class="form-row p12certificatelifetime">
607
              <div class="form-group col-8">
608
              <label for="p12lifetime">Lifetime</label>
609
                <input type="number" name="p12lifetime" id="p12lifetime" ',
610
                'value="', $p12lifetime, '" min="', $minlifetime, '" max="',
611
                $maxlifetime, '" class="form-control" required="required"
612
                aria-describedby="lifetime1help" />
613
                <div class="invalid-tooltip">
614
                  Please enter a valid lifetime for the certificate.
615
                </div>
616
                <small id="lifetime1help" class="form-text text-muted">',
617
                $lifetimetext, '
618
                </small>
619
              </div>
620
              <div class="form-group col-4">
621
                <label for="p12multiplier">&nbsp;</label>
622
                <select id="p12multiplier" name="p12multiplier"
623
                class="form-control">
624
                  <option value="1"',
625
                    (($p12multiplier == 1) ? ' selected="selected"' : ''),
626
                    '>hours</option>
627
                  <option value="24"',
628
                    (($p12multiplier == 24) ? ' selected="selected"' : ''),
629
                    '>days</option>
630
                  <option value="732"',
631
                    (($p12multiplier == 732) ? ' selected="selected"' : ''),
632
                    '>months</option>
633
                </select>
634
              </div>
635
            </div> <!-- end form-row -->
636
637
            <div class="form-group">
638
              <div class="form-row align-items-center">
639
                <div class="col text-center">
640
                  <input type="submit" name="submit"
641
                  class="btn btn-primary submit"
642
                  value="Get New Certificate" onclick="showHourglass(\'p12\')"/>
643
                  <div class="spinner-border"
644
                  style="width: 32px; height: 32px;"
645
                  role="status" id="p12hourglass">
646
                    <span class="sr-only">Generating...</span>
647
                  </div> <!-- spinner-border -->
648
                </div>
649
              </div>
650
            </div>
651
652
            <div class="form-group">
653
              <div class="form-row align-items-center">
654
                <div class="col text-center" id="p12value">
655
                ', $p12link, '
656
                </div>
657
              </div>
658
              <div class="form-row align-items-center">
659
                <div class="col text-center" id="p12expire">
660
                ', $p12expire, '
661
                </div>
662
              </div>
663
            </div>
664
            </form>
665
          </div> <!-- end card-body -->';
666
            static::printCollapseEnd();
667
        }
668
    }
669
670
    /**
671
     * printCertInfo
672
     *
673
     * This function prints information related to the X.509 certificate
674
     * such as DN (distinguished name) and LOA (level of assurance).
675
     */
676
    public static function printCertInfo()
677
    {
678
        $dn = Util::getSessionVar('distinguished_name');
679
        static::printCollapseBegin('certinfo', 'Certificate Information');
680
        if (strlen($dn) > 0) {
681
            // Strip off the email address from the pseudo-DN.
682
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
683
            echo '
684
              <div class="card-body">
685
                <table class="table table-striped table-sm">
686
                <tbody>
687
                  <tr>
688
                    <th>Certificate Subject:</th>
689
                    <td>', Util::htmlent($dn), '</td>
690
                  </tr>
691
                  <tr>
692
                    <th>Identity Provider:</th>
693
                    <td>', Util::getSessionVar('idp_display_name'), '</td>
694
                  </tr>
695
                  <tr>
696
                    <th><a target="_blank"
697
                      href="http://ca.cilogon.org/loa">Level of Assurance:</a></th>
698
                      <td>
699
            ';
700
701
            if (Util::getSessionVar('loa') == 'openid') {
702
                echo '<a href="http://ca.cilogon.org/policy/openid"
703
                      target="_blank">OpenID</a>';
704
            } elseif (Util::isLOASilver()) {
705
                echo '<a href="http://ca.cilogon.org/policy/silver"
706
                      target="_blank">Silver</a>';
707
            } else {
708
                echo '<a href="http://ca.cilogon.org/policy/basic"
709
                      target="_blank">Basic</a>';
710
            }
711
712
            echo '</td>
713
                  </tr>
714
                  </tbody>
715
                </table>';
716
        } else { // No DN? Show missing name(s) or email address.
717
            echo '
718
              <div class="card-body">
719
            ';
720
            static::printErrorBox(
721
                '
722
                <div class="card-text my-2">
723
                  Unable to generate a certificate. Your identity provider
724
                  has not provided CILogon with all required information.
725
                </div> <!-- end card-text -->'
726
            );
727
            $first_name   = Util::getSessionVar('first_name');
728
            $last_name    = Util::getSessionVar('last_name');
729
            $display_name = Util::getSessionVar('display_name');
730
            $email        = Util::getSessionVar('email');
731
            echo '
732
                <table class="table table-striped table-sm">
733
                <tbody>';
734
            if ((strlen($first_name) == 0) && (strlen($display_name) == 0)) {
735
                echo '
736
                  <tr>
737
                    <th class="w-50">First Name:</th>
738
                    <td>MISSING</td>
739
                  </tr>';
740
            }
741
            if ((strlen($last_name) == 0) && (strlen($display_name) == 0)) {
742
                echo '
743
                  <tr>
744
                    <th class="w-50">Last Name:</th>
745
                    <td>MISSING</td>
746
                  </tr>';
747
            }
748
            if (
749
                (strlen($display_name) == 0) &&
750
                ((strlen($first_name) == 0) || (strlen($last_name) == 0))
751
            ) {
752
                echo '
753
                  <tr>
754
                    <th class="w-50">Display Name:</th>
755
                    <td>MISSING</td>
756
                  </tr>';
757
            }
758
            $emailvalid = filter_var($email, FILTER_VALIDATE_EMAIL);
759
            if ((strlen($email) == 0) || (!$emailvalid)) {
760
                echo '
761
                  <tr>
762
                    <th class="w-50">Email Address:</th>
763
                    <td>' , ((strlen($email) == 0) ? 'MISSING' : 'INVALID') , '</td>
764
                  </tr>';
765
            }
766
            $idp     = Util::getSessionVar('idp');
767
            $idp_display_name = Util::getSessionVar('idp_display_name');
768
            if (Util::isEduGAINAndGetCert($idp, $idp_display_name)) {
769
                $idplist = Util::getIdpList();
770
                if (!$idplist->isREFEDSRandS($idp)) {
771
                    echo '
772
                      <tr>
773
                        <th class="w-50"><a target="_blank"
774
                        href="http://refeds.org/category/research-and-scholarship">Research
775
                        and Scholarship</a>:</th>
776
                        <td>MISSING</td>
777
                      </tr>';
778
                }
779
                if (!$idplist->isSIRTFI($idp)) {
780
                    echo '
781
                      <tr>
782
                        <th class="w-50"><a target="_blank"
783
                        href="https://refeds.org/sirtfi">SIRTFI</a>:</th>
784
                        <td>MISSING</td>
785
                      </tr>';
786
                }
787
            }
788
            echo '
789
                  </tbody>
790
                </table>';
791
        }
792
        echo '
793
              </div> <!-- end card-body -->';
794
        static::printCollapseEnd();
795
    }
796
797
    /**
798
     * printUserAttributes
799
     *
800
     * This function shows the user the attributes released by their
801
     * selected IdP and saved in the PHP session.
802
     */
803
    public static function printUserAttributes()
804
    {
805
        $idplist = Util::getIdpList();
806
        $idp = Util::getSessionVar('idp');
807
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
808
809
        // Set various booleans for warning/error messages early so that we
810
        // can display a "general" warning/error icon on the card title.
811
        $warnings = array();
812
        $errors = array();
813
814
        // CIL-416 Show warning for missing ePPN
815
        if (($samlidp) && (empty(Util::getSessionVar('eppn')))) {
816
            if (empty(Util::getSessionVar('eptid'))) {
817
                $errors['no_eppn_or_eptid'] = true;
818
            } else {
819
                $warnings['no_eppn'] = true;
820
            }
821
        }
822
823
        if (empty($idp)) {
824
            $errors['no_entityID'] = true;
825
        } else {
826
            if ((!$samlidp) && (empty(Util::getSessionVar('oidc')))) {
827
                $errors['no_oidc'] = true;
828
            }
829
        }
830
831
        if ((empty(Util::getSessionVar('first_name'))) && (empty(Util::getSessionVar('display_name')))) {
832
            $errors['no_first_name'] = true;
833
        }
834
835
        if ((empty(Util::getSessionVar('last_name'))) && (empty(Util::getSessionVar('display_name')))) {
836
            $errors['no_last_name'] = true;
837
        }
838
839
        if (
840
            (empty(Util::getSessionVar('display_name'))) &&
841
            ((empty(Util::getSessionVar('first_name'))) ||
842
            (empty(Util::getSessionVar('last_name'))))
843
        ) {
844
            $errors['no_display_name'] = true;
845
        }
846
847
        $emailvalid = filter_var(Util::getSessionVar('email'), FILTER_VALIDATE_EMAIL);
848
        if ((empty(Util::getSessionVar('email'))) || (!$emailvalid)) {
849
            $errors['no_valid_email'] = true;
850
        }
851
852
        static::printCollapseBegin(
853
            'userattrs',
854
            'User Attributes ' .
855
            (
856
                (!empty($errors)) ? static::getIcon(
857
                    'fa-exclamation-circle',
858
                    'red',
859
                    'One or more missing attributes.'
860
                ) : ((@$warnings['no_eppn']) ?  static::getIcon(
861
                    'fa-exclamation-triangle',
862
                    'gold',
863
                    'Some CILogon clients (e.g., Globus) require ePPN.'
864
                ) : '')
865
            )
866
        );
867
868
        echo '
869
          <div class="card-body">
870
            <table class="table table-striped table-sm">
871
            <tbody>
872
              <tr>
873
                <th>Identity Provider (entityID):</th>
874
                <td>', $idp , '</td>
875
                <td>';
876
877
        if (@$errors['no_entityID']) {
878
            echo static::getIcon(
879
                'fa-exclamation-circle',
880
                'red',
881
                'Missing the entityID of the IdP.'
882
            );
883
        }
884
885
        echo '
886
                </td>
887
              </tr>
888
889
              <tr>
890
                <th>ePTID:</th>
891
                <td>', Util::getSessionVar('eptid'), '</td>
892
                <td>';
893
894
        if (@$errors['no_eppn_or_eptid']) {
895
            echo static::getIcon(
896
                'fa-exclamation-circle',
897
                'red',
898
                'Must have either ePPN -OR- ePTID.'
899
            );
900
        }
901
902
        echo '
903
                </td>
904
              </tr>
905
906
              <tr>
907
                <th>ePPN:</th>
908
                <td>', Util::getSessionVar('eppn'), '</td>
909
                <td>';
910
911
        if (@$errors['no_eppn_or_eptid']) {
912
            echo static::getIcon(
913
                'fa-exclamation-circle',
914
                'red',
915
                'Must have either ePPN -OR- ePTID.'
916
            );
917
        } elseif (@$warnings['no_eppn']) {
918
            echo static::getIcon(
919
                'fa-exclamation-triangle',
920
                'gold',
921
                'Some CILogon clients (e.g., Globus) require ePPN.'
922
            );
923
        }
924
925
        echo '
926
                </td>
927
              </tr>
928
929
              <tr>
930
                <th>OpenID:</th>
931
                <td>', Util::getSessionVar('oidc'), '</td>
932
                <td>';
933
934
        if (@$errors['no_oidc']) {
935
            echo static::getIcon(
936
                'fa-exclamation-circle',
937
                'red',
938
                'Missing the OpenID identifier.'
939
            );
940
        }
941
942
        echo '
943
                </td>
944
              </tr>
945
946
              <tr>
947
                <th>First Name (givenName):</th>
948
                <td>', Util::getSessionVar('first_name'), '</td>
949
                <td>';
950
951
        if (@$errors['no_first_name']) {
952
            echo static::getIcon(
953
                'fa-exclamation-circle',
954
                'red',
955
                'Must have either givenName + sn -OR- displayName.'
956
            );
957
        }
958
959
        echo '
960
                </td>
961
              </tr>
962
963
              <tr>
964
                <th>Last Name (sn):</th>
965
                <td>', Util::getSessionVar('last_name'), '</td>
966
                <td>';
967
968
        if (@$errors['no_last_name']) {
969
            echo static::getIcon(
970
                'fa-exclamation-circle',
971
                'red',
972
                'Must have either givenName + sn -OR- displayName.'
973
            );
974
        }
975
976
        echo '
977
                </td>
978
              </tr>
979
980
              <tr>
981
                <th>Display Name (displayName):</th>
982
                <td>', Util::getSessionVar('display_name'), '</td>
983
                <td>';
984
985
        if (@$errors['no_display_name']) {
986
            echo static::getIcon(
987
                'fa-exclamation-circle',
988
                'red',
989
                'Must have either displayName -OR- givenName + sn.'
990
            );
991
        }
992
993
        echo '
994
                </td>
995
              </tr>
996
997
              <tr>
998
                <th>Email Address (email):</th>
999
                <td>', Util::getSessionVar('email'), '</td>
1000
                <td>';
1001
1002
        if (@$errors['no_valid_email']) {
1003
            echo static::getIcon(
1004
                'fa-exclamation-circle',
1005
                'red',
1006
                'Missing valid email address.'
1007
            );
1008
        }
1009
1010
        echo '
1011
                </td>
1012
              </tr>
1013
1014
              <tr>
1015
                <th>Level of Assurance (assurance):</th>
1016
                <td>', Util::getSessionVar('loa'), '</td>
1017
                <td> </td>
1018
              </tr>
1019
1020
              <tr>
1021
                <th>AuthnContextClassRef:</th>
1022
                <td>', Util::getSessionVar('acr'), '</td>
1023
                <td> </td>
1024
              </tr>
1025
1026
              <tr>
1027
                <th>Affiliation (affiliation):</th>
1028
                <td>', Util::getSessionVar('affiliation'), '</td>
1029
                <td> </td>
1030
              </tr>
1031
1032
              <tr>
1033
                <th>Entitlement (entitlement):</th>
1034
                <td>', Util::getSessionVar('entitlement'), '</td>
1035
                <td> </td>
1036
              </tr>
1037
1038
              <tr>
1039
                <th>Organizational Unit (ou):</th>
1040
                <td>', Util::getSessionVar('ou'), '</td>
1041
                <td> </td>
1042
              </tr>
1043
1044
              <tr>
1045
                <th>Member (member):</th>
1046
                <td>', Util::getSessionVar('member_of'), '</td>
1047
                <td> </td>
1048
              </tr>
1049
1050
              <tr>
1051
                <th>iTrustUIN (itrustuin):</th>
1052
                <td>', Util::getSessionVar('itrustuin'), '</td>
1053
                <td> </td>
1054
              </tr>
1055
1056
              <tr>
1057
                <th>Subject ID (subject-id):</th>
1058
                <td>', Util::getSessionVar('subject_id'), '</td>
1059
                <td> </td>
1060
              </tr>
1061
1062
              <tr>
1063
                <th>Pairwise ID (pairwise-id):</th>
1064
                <td>', Util::getSessionVar('pairwise_id'), '</td>
1065
                <td> </td>
1066
              </tr>
1067
              </tbody>
1068
            </table>
1069
          </div> <!-- end card-body -->';
1070
        static::printCollapseEnd();
1071
    }
1072
1073
    /**
1074
     * printIdPMetadata
1075
     *
1076
     * This function shows the metadata associated with the IdP saved to
1077
     * the PHP session.
1078
     */
1079
    public static function printIdPMetadata()
1080
    {
1081
        $idplist = Util::getIdpList();
1082
        $idp = Util::getSessionVar('idp');
1083
        $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
1084
        $shibarray = $idplist->getShibInfo($idp);
1085
1086
        // CIL-416 Check for eduGAIN IdPs without both REFEDS R&S and SIRTFI
1087
        // since these IdPs are not allowed to get certificates.
1088
        $eduGainWithoutRandSandSIRTFI = 0;
1089
        if (
1090
            ($samlidp) &&
1091
            (!$idplist->isRegisteredByInCommon($idp)) &&
1092
            ((!$idplist->isREFEDSRandS($idp)) ||
1093
             (!$idplist->isSIRTFI($idp)))
1094
        ) {
1095
            $eduGainWithoutRandSandSIRTFI = 1;
1096
        }
1097
1098
        static::printCollapseBegin(
1099
            'idpmeta',
1100
            'Identity Provider Attributes ' .
1101
            (
1102
                // CIL-416 Show warning for missing ePPN
1103
                ($eduGainWithoutRandSandSIRTFI) ?
1104
                static::getIcon(
1105
                    'fa-exclamation-triangle',
1106
                    'gold',
1107
                    'This IdP does not support both ' .
1108
                    'REFEDS R&amp;S and SIRTFI. CILogon ' .
1109
                    'functionality may be limited.'
1110
                ) : ''
1111
            )
1112
        );
1113
1114
        echo'
1115
          <div class="card-body">
1116
            <table class="table table-striped table-sm">
1117
            <tbody>
1118
              <tr>
1119
                <th>Organization Name:</th>
1120
                <td>', @$shibarray['Organization Name'] , '</td>
1121
                <td>';
1122
1123
        if (empty(@$shibarray['Organization Name'])) {
1124
            echo static::getIcon(
1125
                'fa-exclamation-circle',
1126
                'red',
1127
                'Could not find ' .
1128
                '&lt;OrganizationDisplayName&gt; in metadata.'
1129
            );
1130
        }
1131
1132
        echo '
1133
                </td>
1134
              </tr>
1135
              <tr>
1136
                <th>Home Page:</th>
1137
                <td><a target="_blank" href="', @$shibarray['Home Page'] , '">',
1138
                @$shibarray['Home Page'] , '</a></td>
1139
                <td> </td>
1140
              </tr>
1141
1142
              <tr>
1143
                <th>Support Contact:</th>';
1144
        if (
1145
            (!empty(@$shibarray['Support Name'])) ||
1146
            (!empty(@$shibarray['Support Address']))
1147
        ) {
1148
            echo '
1149
                <td>', @$shibarray['Support Name'] , ' &lt;',
1150
                        preg_replace('/^mailto:/', '', @$shibarray['Support Address']), '&gt;</td>
1151
                <td> </td>';
1152
        }
1153
        echo '
1154
              </tr>
1155
1156
        ';
1157
1158
        if ($samlidp) {
1159
            echo '
1160
              <tr>
1161
                <th>Technical Contact:</th>';
1162
            if (
1163
                (!empty(@$shibarray['Technical Name'])) ||
1164
                (!empty(@$shibarray['Technical Address']))
1165
            ) {
1166
                echo '
1167
                <td>', @$shibarray['Technical Name'] , ' &lt;',
1168
                        preg_replace('/^mailto:/', '', @$shibarray['Technical Address']), '&gt;</td>
1169
                <td> </td>';
1170
            }
1171
            echo '
1172
              </tr>
1173
1174
              <tr>
1175
                <th>Administrative Contact:</th>';
1176
            if (
1177
                (!empty(@$shibarray['Administrative Name'])) ||
1178
                (!empty(@$shibarray['Administrative Address']))
1179
            ) {
1180
                echo '
1181
                <td>', @$shibarray['Administrative Name'] , ' &lt;',
1182
                        preg_replace('/^mailto:/', '', @$shibarray['Administrative Address']), '&gt;</td>
1183
                <td> </td>';
1184
            }
1185
            echo '
1186
              </tr>
1187
1188
              <tr>
1189
                <th>Registered by InCommon:</th>
1190
                <td>', ($idplist->isRegisteredByInCommon($idp) ? 'Yes' : 'No'), '</td>
1191
                <td> </td>
1192
              </tr>
1193
1194
              <tr>
1195
                <th><a style="text-decoration:underline" target="_blank"
1196
                href="http://id.incommon.org/category/research-and-scholarship">InCommon R
1197
                &amp; S</a>:</th>
1198
                <td>', ($idplist->isInCommonRandS($idp) ? 'Yes' : 'No'), '</td>
1199
                <td> </td>
1200
              </tr>
1201
1202
              <tr>
1203
                <th><a style="text-decoration:underline" target="_blank"
1204
                href="http://refeds.org/category/research-and-scholarship">REFEDS
1205
                R &amp; S</a>:</th>
1206
                <td>', ($idplist->isREFEDSRandS($idp) ? 'Yes' : 'No'), '</td>
1207
                <td>';
1208
1209
            if (
1210
                ($eduGainWithoutRandSandSIRTFI &&
1211
                !$idplist->isREFEDSRandS($idp))
1212
            ) {
1213
                echo static::getIcon(
1214
                    'fa-exclamation-triangle',
1215
                    'gold',
1216
                    'This IdP does not support both ' .
1217
                    'REFEDS R&amp;S and SIRTFI. ' .
1218
                    'CILogon functionality may be limited.'
1219
                );
1220
            }
1221
1222
            echo '
1223
                </td>
1224
              </tr>
1225
1226
              <tr>
1227
                <th><a style="text-decoration:underline" target="_blank"
1228
                       href="https://refeds.org/sirtfi">SIRTFI</a>:</th>
1229
                <td>', ($idplist->isSIRTFI($idp) ? 'Yes' : 'No'), '</td>
1230
                <td>';
1231
1232
            if (
1233
                ($eduGainWithoutRandSandSIRTFI &&
1234
                !$idplist->isSIRTFI($idp))
1235
            ) {
1236
                echo static::getIcon(
1237
                    'fa-exclamation-triangle',
1238
                    'gold',
1239
                    'This IdP does not support both ' .
1240
                    'REFEDS R&amp;S and SIRTFI. ' .
1241
                    'CILogon functionality may be limited.'
1242
                );
1243
            }
1244
1245
            echo '
1246
                </td>
1247
              </tr>
1248
1249
              <tr>
1250
                <th><a style="text-decoration:underline" target="_blank"
1251
                href="http://id.incommon.org/assurance/bronze">InCommon Bronze</a>:</th>
1252
                <td>', ($idplist->isBronze($idp) ? 'Yes' : 'No'), '</td>
1253
                <td> </td>
1254
              </tr>
1255
1256
              <tr>
1257
                <th><a style="text-decoration:underline" target="_blank"
1258
                href="http://id.incommon.org/assurance/silver">InCommon Silver</a>:</th>
1259
                <td>', ($idplist->isSilver($idp) ? 'Yes' : 'No'), '</td>
1260
                <td> </td>
1261
              </tr>
1262
1263
              <tr>
1264
                <th>Entity ID</th>
1265
                <td><a style="text-decoration:underline" target="_blank"
1266
                href="https://met.refeds.org/met/entity/',
1267
                rawurlencode($idp),
1268
                '">', $idp, '</a></td>
1269
                <td> </td>
1270
              </tr>
1271
            ';
1272
        } // end if ($samlidp)
1273
1274
            echo '
1275
              </tbody>
1276
            </table>
1277
          </div> <!-- end card-body -->';
1278
        static::printCollapseEnd();
1279
    }
1280
1281
    /**
1282
     * getIcon
1283
     *
1284
     * This function returns the HTML for the Font Awesome icons which can
1285
     * appear inline with other information.  This is accomplished via the
1286
     * use of wrapping the image in a <span> tag.
1287
     *
1288
     * @param string $icon The Font Awesome icon to be shown.
1289
     * @param string $color The HMTL color for the icon.
1290
     * @param string $help (Optionals) The popup 'title' help text to be
1291
     *        displayed when the mouse cursor hovers over the icon.
1292
     *        Defaults to empty string.
1293
     * @return string HTML for the icon block to output.
1294
     */
1295
    public static function getIcon($icon, $color, $help = '')
1296
    {
1297
        return '<span style="color: ' . $color . ';
1298
            -webkit-text-stroke-width: 1px;
1299
            -webkit-text-stroke-color: gray;">' .
1300
            ((strlen($help) > 0) ? '<span data-trigger="hover" ' .
1301
            'data-toggle="popover" data-html="true" ' .
1302
            'data-content="' . $help . '">' : '') .
1303
            '<i class="fa nocollapse ' . $icon . '"></i>' .
1304
            ((strlen($help) > 0) ? '</span>' : '') .
1305
            '</span>';
1306
    }
1307
1308
    /**
1309
     * printCollapseBegin
1310
     *
1311
     * This function prints the preamble for a collapsible Bootstrap Card.
1312
     *
1313
     * @param string $name The name to give to the collapse elements which
1314
     *        should be unique among all collapse elements on the page.
1315
     * @param string $title The text for the card-header.
1316
     * @param bool $collapsed (optional) If true, then start with the card
1317
     *        collapsed. If false, start with the card opened.
1318
     */
1319
    public static function printCollapseBegin($name, $title, $collapsed = true)
1320
    {
1321
        echo '
1322
      <div class="card col-sm-10 offset-sm-1">
1323
        <h5 class="card-header text-center">
1324
          <a class="d-block',
1325
            ($collapsed ? ' collapsed' : ''),
1326
            '" data-toggle="collapse"
1327
            href="#collapse-', $name, '" aria-expanded="',
1328
            ($collapsed ? 'false' : "true"),
1329
            '" aria-controls="collapse-', $name, '"
1330
            id="heading-', $name, '">
1331
            <i class="fa fa-chevron-down pull-right"></i>
1332
            ', $title, '
1333
          </a>
1334
        </h5>
1335
        <div id="collapse-',$name, '" class="collapse',
1336
        ($collapsed ? '' : ' show'),
1337
        '" aria-labelledby="heading-', $name , '">';
1338
    }
1339
1340
    /**
1341
     * printCollapseEnd
1342
     *
1343
     * This function prints the closing block corresponding to the
1344
     * printCollapseBegin.
1345
     */
1346
    public static function printCollapseEnd()
1347
    {
1348
        echo '
1349
        </div> <!-- end collapse-... -->
1350
      </div> <!-- end card -->
1351
        ';
1352
    }
1353
1354
    /**
1355
     * printErrorBox
1356
     *
1357
     * This function prints out a bordered box with an error icon and any
1358
     * passed-in error HTML text.  The error icon and text are output to
1359
     * a <table> so as to keep the icon to the left of the error text.
1360
     *
1361
     * @param string $errortext HTML error text to be output
1362
     */
1363
    public static function printErrorBox($errortext)
1364
    {
1365
        echo '
1366
        <div class="alert alert-danger" role="alert">
1367
          <div class="row">
1368
            <div class="col-1 align-self-center text-center">
1369
            ', static::getIcon('fa-exclamation-circle fa-2x', 'red'),'
1370
            </div>
1371
            <div class="col">
1372
            ', $errortext , '
1373
            </div>
1374
          </div>
1375
        </div>
1376
        ';
1377
    }
1378
1379
    /**
1380
     * printNoScript
1381
     *
1382
     * This function prints the <NoScript> block which is displayed if the
1383
     * user's browser does not have JavaScript enabled.
1384
     */
1385
    public static function printNoScript()
1386
    {
1387
        echo'
1388
      <noscript>
1389
        <div class="alert alert-danger alert-dismissible" role="alert">
1390
          <span><strong>Notice: </strong> JavaScript is not enabled.
1391
          The CILogon Service requires JavaScript for functionality.
1392
          <a target="_blank" href="https://enable-javascript.com/"
1393
          class="alert-link">Please Enable JavaScript</a>.</span>
1394
        </div>
1395
      </noscript>
1396
        ';
1397
    }
1398
1399
    /**
1400
     * printLogOff
1401
     *
1402
     * This function prints the Log Of boxes at the bottom of the main page.
1403
     */
1404
    public static function printLogOff()
1405
    {
1406
        $logofftext = 'End your CILogon session and return to the ' .
1407
           'front page. Note that this will not log you out at ' .
1408
            Util::getSessionVar('idp_display_name') . '.';
1409
1410
        static::printFormHead();
1411
        echo '
1412
          <div class="form-group mt-3">
1413
            <div class="form-row align-items-center">
1414
              <div class="col text-center">
1415
              ';
1416
1417
        $logofftextbox = Util::getSkin()->getConfigOption('logofftextbox');
1418
        if ((!is_null($logofftextbox)) && ((int)$logofftextbox == 1)) {
1419
            echo '  <div class="btn btn-primary">To log off,
1420
                please quit your browser.</div>';
1421
        } else {
1422
            echo '  <input type="submit" name="submit"
1423
                class="btn btn-primary submit"
1424
                title="', $logofftext , '" value="Log Off" />';
1425
        }
1426
1427
        echo '
1428
              </div> <!-- end col-auto -->
1429
            </div> <!-- end form-row align-items-center -->
1430
          </div> <!-- end form-group -->
1431
        </form>
1432
        ';
1433
    }
1434
1435
    /**
1436
     * printGeneralErrorPage
1437
     *
1438
     * This is a convenience method called by handleGotUser to print out
1439
     * a general error page to the user.
1440
     *
1441
     * @param string $redirect The url for the <form> element
1442
     * @param string $redirectform Additional hidden input fields for the
1443
     *        <form>.
1444
     */
1445
    public static function printGeneralErrorPage($redirect, $redirectform)
1446
    {
1447
        Util::unsetAllUserSessionVars();
1448
1449
        static::printHeader('Error Logging On');
1450
        static::printCollapseBegin(
1451
            'attributeerror',
1452
            'General Error',
1453
            false
1454
        );
1455
1456
        echo '
1457
              <div class="card-body px-5">';
1458
1459
        static::printErrorBox('An error has occurred. System
1460
            administrators have been notified. This may be a temporary
1461
            error. Please try again later, or contact us at the the email
1462
            address at the bottom of the page.');
1463
1464
        static::printFormHead($redirect, 'get');
1465
1466
        echo '
1467
              <div class="card-text my-2">
1468
                <div class="form-group">
1469
                  <div class="form-row align-items-center
1470
                  justify-content-center">
1471
                    <div class="col-auto">
1472
                      ', $redirectform, '
1473
                      <input type="submit" name="submit"
1474
                      class="btn btn-primary submit form-control"
1475
                      value="Proceed" />
1476
                    </div>
1477
                  </div> <!-- end form-row align-items-center -->
1478
                </div> <!-- end form-group -->
1479
              </div> <!-- end card-text -->
1480
            </form>
1481
            </div> <!-- end card-body -->';
1482
1483
        Content::printCollapseEnd();
1484
        Content::printFooter();
1485
    }
1486
1487
    /**
1488
     * printSAMLAttributeReleaseErrorPage
1489
     *
1490
     * This is a convenience method called by handleGotUser to print out
1491
     * the attribute release error page for SAML IdPs. This can occur when
1492
     * not all attributes were released by the IdP, or when the IdP is an
1493
     * eduGAIN IdP without both R&S and SIRTFI, and the user was trying to
1494
     * get a certificate.
1495
     *
1496
     * @param string $eppn
1497
     * @param string $eptid
1498
     * @param string $first_name
1499
     * @param string $last_name
1500
     * @param string $display_name
1501
     * @param string $email
1502
     * @param string $idp
1503
     * @param string $idp_display_name
1504
     * @param string $affiliation
1505
     * @param string $ou
1506
     * @param string $member_of
1507
     * @param string $acr
1508
     * @param string $entitlement
1509
     * @param string $itrustuin
1510
     * @param string $subject_id
1511
     * @param string $pairwise_id
1512
     * @param string $clientparams
1513
     * @param string $redirect The url for the <form> element
1514
     * @param string $redirectform Additional hidden input fields for the
1515
     *        <form>.
1516
     * @param bool   $edugainandgetcert Is the IdP in eduGAIN without both
1517
     *        R&S and SIRTIF, and the user could get a certificate?
1518
     */
1519
    public static function printSAMLAttributeReleaseErrorPage(
1520
        $eppn,
1521
        $eptid,
1522
        $first_name,
1523
        $last_name,
1524
        $display_name,
1525
        $email,
1526
        $idp,
1527
        $idp_display_name,
1528
        $affiliation,
1529
        $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

1529
        /** @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...
1530
        $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

1530
        /** @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...
1531
        $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

1531
        /** @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...
1532
        $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

1532
        /** @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...
1533
        $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

1533
        /** @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...
1534
        $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

1534
        /** @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...
1535
        $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

1535
        /** @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...
1536
        $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

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

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

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

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

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

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

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

2433
                /** @scrutinizer ignore-call */ 
2434
                printLogonPage();
Loading history...
2434
            } else { // Got user successfully
2435
                static::gotUserSuccess();
2436
            }
2437
        }
2438
    }
2439
2440
    /**
2441
     * gotUserSuccess
2442
     *
2443
     * This function is called after the user has been successfully
2444
     * authenticated. If the 'status' session variable is STATUS_OK
2445
     * then it checks if we have a new or changed user and logs
2446
     * that appropriately. It then continues to the MainPage.
2447
     */
2448
    public static function gotUserSuccess()
2449
    {
2450
        $log = new Loggit();
2451
        $status = Util::getSessionVar('status');
2452
2453
        // If this is the first time the user has used the CILogon Service,
2454
        // and the flow is OAuth-based, send an alert if the name contains
2455
        // any HTML entities.
2456
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2457
        $callbackuri = Util::getSessionVar('callbackuri');
2458
2459
        if (
2460
            ($status == DBService::$STATUS['STATUS_NEW_USER']) &&
2461
            ((strlen($callbackuri) > 0) ||
2462
             (isset($clientparams['code'])))
2463
        ) {
2464
            // Extra check for new users: see if any HTML entities
2465
            // are in the user name. If so, send an email alert.
2466
            $dn = Util::getSessionVar('distinguished_name');
2467
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
2468
            $htmldn = Util::htmlent($dn);
2469
            if (strcmp($dn, $htmldn) != 0) {
2470
                Util::sendErrorAlert(
2471
                    'New user DN contains HTML entities',
2472
                    "htmlentites(DN) = $htmldn\n"
2473
                );
2474
            }
2475
        }
2476
2477
        // For a new user, or if the user got new attributes, just log it.
2478
        // Then proceed to the Main Page.
2479
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
2480
            $log->info('New User.');
2481
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
2482
            $log->info('User IdP attributes changed.');
2483
        }
2484
        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

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