Passed
Push — master ( fca0dd...36b196 )
by Terrence
14:21
created

Content::printCertInfo()   D

Complexity

Conditions 19
Paths 84

Size

Total Lines 124
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 380

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 19
eloc 51
c 3
b 0
f 0
nc 84
nop 0
dl 0
loc 124
ccs 0
cts 83
cp 0
crap 380
rs 4.5166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

1545
        /** @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...
1546
        $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

1546
        /** @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...
1547
        $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

1547
        /** @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...
1548
        $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

1548
        /** @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...
1549
        $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

1549
        /** @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...
1550
        $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

1550
        /** @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...
1551
        $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

1551
        /** @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...
1552
        $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

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

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

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

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

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

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

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

2460
                /** @scrutinizer ignore-call */ 
2461
                printLogonPage();
Loading history...
2461
            } else { // Got user successfully
2462
                static::gotUserSuccess();
2463
            }
2464
        }
2465
    }
2466
2467
    /**
2468
     * gotUserSuccess
2469
     *
2470
     * This function is called after the user has been successfully
2471
     * authenticated. If the 'status' session variable is STATUS_OK
2472
     * then it checks if we have a new or changed user and logs
2473
     * that appropriately. It then continues to the MainPage.
2474
     */
2475
    public static function gotUserSuccess()
2476
    {
2477
        $log = new Loggit();
2478
        $status = Util::getSessionVar('status');
2479
2480
        // If this is the first time the user has used the CILogon Service,
2481
        // and the flow is OAuth-based, send an alert if the name contains
2482
        // any HTML entities.
2483
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2484
        $callbackuri = Util::getSessionVar('callbackuri');
2485
2486
        // Log new users with possibly empty distinguished_name values
2487
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
2488
            $dn = Util::getSessionVar('distinguished_name');
2489
            $log->info('New User' . ((strlen($dn) == 0) ? ' without a distinguished_name.' : '.'));
2490
            // If HTML entities are in the distinguished_name, send an alert.
2491
            if (
2492
                (strlen($dn) > 0) &&
2493
                ((strlen($callbackuri) > 0) ||
2494
                 (isset($clientparams['code'])))
2495
            ) {
2496
                $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
2497
                $htmldn = Util::htmlent($dn);
2498
                if (strcmp($dn, $htmldn) != 0) {
2499
                    Util::sendErrorAlert(
2500
                        'New user DN contains HTML entities',
2501
                        "htmlentites(DN) = $htmldn\n"
2502
                    );
2503
                }
2504
            }
2505
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
2506
            $log->info('User IdP attributes changed.');
2507
        }
2508
        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

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