Passed
Push — master ( 380a00...3979db )
by Terrence
12:18
created

Content::printCollapseBegin()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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

1555
        /** @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...
1556
        $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

1556
        /** @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...
1557
        $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

1557
        /** @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...
1558
        $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

1558
        /** @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...
1559
        $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

1559
        /** @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...
1560
        $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

1560
        /** @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...
1561
        $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

1561
        /** @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...
1562
        $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

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

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

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

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

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

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

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

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

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

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
2940
     *        OAuth2 IdPs supported by CILogon.
2941
     * @return string The input string transformed to a URL to be used in
2942
     *         the 'Select an Identity Provider' list. If the incoming
2943
     *         string does not match one of the OAuth2 issuers, the string
2944
     *         is returned unmodified.
2945
     */
2946
    public static function normalizeOAuth2IdP($idp)
2947
    {
2948
        if (preg_match('%^https?://accounts.google.com%', $idp)) {
2949
            $idp = 'https://accounts.google.com/o/oauth2/auth';
2950
        } elseif (preg_match('%^https?://github.com%', $idp)) {
2951
            $idp = 'https://github.com/login/oauth/authorize';
2952
        } elseif (preg_match('%^https?://orcid.org%', $idp)) {
2953
            $idp = 'https://orcid.org/oauth/authorize';
2954
        }
2955
        return $idp;
2956
    }
2957
}
2958