Content::printGetCertificate()   F
last analyzed

Complexity

Conditions 42
Paths 6945

Size

Total Lines 274
Code Lines 148

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1806

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 42
eloc 148
nc 6945
nop 0
dl 0
loc 274
ccs 0
cts 238
cp 0
crap 1806
rs 0
c 4
b 1
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\Util;
6
use CILogon\Service\MyProxy;
7
use CILogon\Service\PortalCookie;
8
use CILogon\Service\DBService;
9
use CILogon\Service\OAuth2Provider;
10
use CILogon\Service\Loggit;
11
use Net_LDAP2_Util;
12
13
/**
14
 * Content
15
 */
16
class Content
17
{
18
    /**
19
     * printHeader
20
     *
21
     * This function should be called to print out the main HTML header
22
     * block for each web page.  This gives a consistent look to the site.
23
     * Any style changes should go in the cilogon.css file.
24
     *
25
     * @param string $title The text in the window's titlebar
26
     * @param bool $csrfcookie Set the CSRF cookie. Defaults to true.
27
     */
28
    public static function printHeader($title = '', $csrfcookie = true)
29
    {
30
        if ($csrfcookie) {
31
            $csrf = Util::getCsrf();
32
            $csrf->setTheCookie();
33
        }
34
35
        // Find the 'Powered By CILogon' image if specified by the skin
36
        $poweredbyimg = "/images/poweredbycilogon.png";
37
        $skin = Util::getSkin();
38
        $skinpoweredbyimg = (string)$skin->getConfigOption('poweredbyimg');
39
        if (
40
            (strlen($skinpoweredbyimg) > 0) &&
41
            (is_readable('/var/www/html' . $skinpoweredbyimg))
42
        ) {
43
            $poweredbyimg = $skinpoweredbyimg;
44
        }
45
46
        echo '<!doctype html>
47
<html lang="en">
48
  <head>
49
    <!-- Required meta tags -->
50
    <meta charset="utf-8" />
51
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
52
53
    <title>', $title, '</title>
54
55
    <!-- Font Awesome CSS -->
56
    <link rel="stylesheet"
57
          href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
58
          integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
59
          crossorigin="anonymous" />
60
    <!-- Bootstrap CSS -->
61
    <link rel="stylesheet"
62
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.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
     * @param bool $showcancel (Optional) Force the display of the 'Cancel'
212
     *        button next to the logon button. Defaults to false, which
213
     *        means the Cancel button is shown based on the skin
214
     *        configuration.
215
     */
216
    public static function printWAYF($showremember = true, $showcancel = false)
217
    {
218
        $idps = static::getCompositeIdPList();
219
        $skin = Util::getSkin();
220
221
        $rememberhelp = 'Check this box to bypass the welcome page on ' .
222
            'subsequent visits and proceed directly to the selected ' .
223
            'identity provider. You will need to clear your browser\'s ' .
224
            'cookies to return here.';
225
        $selecthelp = '<p>
226
            CILogon facilitates secure access to CyberInfrastructure (CI).
227
            In order to use the CILogon Service, you must first select
228
            an identity provider. An identity provider (IdP) is an
229
            organization where you have an account and can log on
230
            to gain access to online services.
231
        </p>
232
        <p>
233
            If you are a faculty, staff, or student member of a university
234
            or college, please select it for your identity provider.
235
            If your school is not listed, please contact <a
236
            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...
237
            and we will try to add your school in the future.
238
        </p>
239
        ';
240
241
        $googleauthz = Util::getAuthzUrl('Google');
242
        if (isset($idps[$googleauthz])) {
243
            $selecthelp .= '<p>If you have a <a target=\'_blank\'
244
            href=\'https://myaccount.google.com\'>Google</a> account,
245
            you can select it for authenticating to the CILogon Service.</p>
246
            ';
247
        }
248
        $githubauthz = Util::getAuthzUrl('GitHub');
249
        if (isset($idps[$githubauthz])) {
250
            $selecthelp .= '<p> If you have a <a target=\'_blank\'
251
            href=\'https://github.com/settings/profile\'>GitHub</a> account,
252
            you can select it for authenticating to the CILogon Service.</p>
253
            ';
254
        }
255
        $orcidauthz = Util::getAuthzUrl('ORCID');
256
        if (isset($idps[$orcidauthz])) {
257
            $selecthelp .= '<p> If you have a <a target=\'_blank\'
258
            href=\'https://orcid.org/my-orcid\'>ORCID</a> account,
259
            you can select it for authenticating to the CILogon Service.</p>
260
            ';
261
        }
262
263
        // Check if the user had previously selected an IdP from the list.
264
        // First, check the portalcookie, then the 'normal' cookie.
265
        $keepidp = '';
266
        $providerId = '';
267
        $pc = new PortalCookie();
268
        $pn = $pc->getPortalName();
269
        if (strlen($pn) > 0) {
270
            $keepidp    = $pc->get('keepidp');
271
            $providerId = $pc->get('providerId');
272
        } else {
273
            $keepidp    = Util::getCookieVar('keepidp');
274
            $providerId = Util::getCookieVar('providerId');
275
        }
276
277
        // CIL-763 If there was no previously saved cookie for the initially
278
        // selected IdP, check for 'initialidp=...' query parameter. Note
279
        // that this value will be overridden by the 'idphint=...' query
280
        // parameter (if also present).
281
        if (
282
            (strlen($providerId) == 0) &&
283
            (!empty(Util::getGetVar('initialidp')))
284
        ) {
285
            $providerId = static::normalizeOAuth2IdP(Util::getGetVar('initialidp'));
286
        }
287
288
        // Make sure previously selected IdP is in list of available IdPs.
289
        if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
290
            $keepidp = '';
291
            $providerId = '';
292
        }
293
294
        // If no previous providerId, get from skin, or default to Google.
295
        if (strlen($providerId) == 0) {
296
            $initialidp = (string)$skin->getConfigOption('initialidp');
297
            if ((strlen($initialidp) > 0) && (isset($idps[$initialidp]))) {
298
                $providerId = $initialidp;
299
            } else {
300
                $providerId = Util::getAuthzUrl('Google');
301
            }
302
        }
303
304
        // Check if an OIDC client selected an IdP for the transaction.
305
        // If so, verify that the IdP is in the list of available IdPs.
306
        $useselectedidp = false;
307
        $idphintlist = static::getIdphintList($idps);
308
        if (!empty($idphintlist)) {
309
            $useselectedidp = true;
310
            $providerId = $idphintlist[0];
311
            $newidps = array();
312
            // Update the IdP selection list to show just the idphintlist.
313
            foreach ($idphintlist as $value) {
314
                $newidps[$value] = $idps[$value];
315
            }
316
            $idps = $newidps;
317
            // Re-sort the $idps by Display_Name for correct alphabetization.
318
            uasort($idps, function ($a, $b) {
319
                return strcasecmp(
320
                    $a['Display_Name'],
321
                    $b['Display_Name']
322
                );
323
            });
324
        }
325
326
        echo '
327
      <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">
328
        <h4 class="card-header">',
329
        ($useselectedidp ? 'Selected' : 'Select an'),
330
        ' Identity Provider</h4>
331
        <div class="card-body">
332
          <form action="', Util::getScriptDir(), '" method="post">
333
            <div class="form-group">
334
            <select name="providerId" id="providerId"
335
                autofocus="autofocus"
336
                class="selectpicker mw-100"
337
                data-size="20" data-width="fit"
338
                data-live-search="true"
339
                data-live-search-placeholder="Type to search"
340
                data-live-search-normalize="true"
341
                >';
342
343
344
        foreach ($idps as $entityId => $names) {
345
            echo '
346
                <option data-tokens="', $entityId , '" value="',
347
                $entityId , '"',
348
                (($entityId == $providerId) ? ' selected="selected"' : ''),
349
            '>', Util::htmlent($names['Display_Name']), '</option>';
350
        }
351
352
        echo '
353
            </select>
354
            <a href="#" tabindex="0" data-trigger="hover click"
355
            class="helpcursor"
356
            data-toggle="popover" data-html="true"
357
            title="Selecting an Identity Provider"
358
            data-content="', $selecthelp, '"><i class="fa
359
            fa-question-circle"></i></a>
360
            </div> <!-- end div form-group -->
361
            ';
362
363
        if ($showremember) {
364
            echo '
365
            <div class="form-group">
366
              <div class="form-check">
367
                <input class="form-check-input" type="checkbox"
368
                id="keepidp" name="keepidp" ',
369
                ((strlen($keepidp) > 0) ? 'checked="checked" ' : ''), ' />
370
                <label class="form-check-label"
371
                for="keepidp">Remember this selection</label>
372
                <a href="#" tabindex="0" data-trigger="hover click"
373
                class="helpcursor"
374
                data-toggle="popover" data-html="true"
375
                data-content="', $rememberhelp, '"><i class="fa
376
                fa-question-circle"></i></a>
377
              </div> <!-- end div form-check -->
378
            </div> <!-- end div form-group -->
379
            ';
380
        }
381
382
        echo Util::getCsrf()->hiddenFormElement();
383
        echo '<input type="hidden" name="previouspage" value="WAYF" />';
384
        $lobtext = static::getLogOnButtonText();
385
386
        echo '
387
            <div class="form-group">
388
              <div class="form-row align-items-center justify-content-center">
389
                <div class="col-auto">
390
                  <input type="submit" name="submit"
391
                  class="btn btn-primary submit"
392
                  value="', $lobtext , '" id="wayflogonbutton" />
393
                </div> <!-- end col-auto -->
394
        ';
395
396
        $wayfcancelbutton = Util::getSkin()->getConfigOption('wayfcancelbutton');
397
        if (
398
            ($showcancel) ||
399
            ((!is_null($wayfcancelbutton)) && ((int)$wayfcancelbutton == 1))
400
        ) {
401
            echo '
402
                <div class="col-auto">
403
                  <input type="submit" name="submit"
404
                  class="btn btn-primary submit"
405
                  title="Cancel authentication."
406
                  value="Cancel" id="wayfcancelbutton" />
407
                </div>
408
            ';
409
        }
410
411
        echo '
412
              </div> <!-- end div form-row align-items-center -->
413
            </div> <!-- end div form-group -->
414
        ';
415
416
        $logonerror = Util::getSessionVar('logonerror');
417
        Util::unsetSessionVar('logonerror');
418
        if (strlen($logonerror) > 0) {
419
            echo '<div class="alert alert-danger" role="alert">', $logonerror, '</div>';
420
        }
421
422
        echo '
423
        <p class="privacypolicy">
424
        By selecting "', $lobtext , '", you agree to <a target="_blank"
425
        href="https://www.cilogon.org/privacy">CILogon\'s privacy
426
        policy</a>.
427
        </p>
428
429
          </form>
430
431
        </div> <!-- End card-body -->
432
      </div> <!-- End card -->
433
        ';
434
    }
435
436
    /**
437
     * printGetCertificate
438
     *
439
     * This function prints the 'Get New Certificate' box on the main page.
440
     * If the 'p12' PHP session variable is valid, it is read and a link for
441
     * the usercred.p12 file is presented to the user.
442
     */
443
    public static function printGetCertificate()
444
    {
445
        // CIL-624 If DISABLE_X509 is true, then don't even print out the
446
        // Get New Certificate box.
447
        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...
448
            return;
449
        }
450
451
        // Check if PKCS12 downloading is disabled. If so, print out message.
452
        $disabledmsg = '';
453
        $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...
454
        $skin = Util::getSkin();
455
        $pkcs12disabled = $skin->getConfigOption('pkcs12', 'disabled');
456
        $disabledbyskin = ((!is_null($pkcs12disabled)) && ((int)$pkcs12disabled == 1));
457
        $dn = Util::getSessionVar('distinguished_name'); // Did we get user attributes for certs?
458
        $isEduGAINAndGetCert = Util::isEduGAINAndGetCert();
459
460
        if ($disabledbyconf || $disabledbyskin || (strlen($dn) == 0) || $isEduGAINAndGetCert) {
461
            if ($disabledbyconf) {
462
                $disabledmsg = 'Downloading PKCS12 certificates is disabled.';
463
            } elseif ($disabledbyskin) {
464
                $disabledmsg = $skin->getConfigOption('pkcs12', 'disabledmessage');
465
                if (!is_null($disabledmsg)) {
466
                    $disabledmsg = trim(html_entity_decode($disabledmsg));
467
                }
468
                if (strlen($disabledmsg) == 0) {
0 ignored issues
show
Bug introduced by
It seems like $disabledmsg can also be of type null; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

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

1582
        /** @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...
1583
        $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

1583
        /** @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...
1584
        $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

1584
        /** @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...
1585
        $amr,
0 ignored issues
show
Unused Code introduced by
The parameter $amr 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

1585
        /** @scrutinizer ignore-unused */ $amr,

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...
1586
        $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

1586
        /** @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...
1587
        $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

1587
        /** @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...
1588
        $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

1588
        /** @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...
1589
        $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

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

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

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

2228
            /** @scrutinizer ignore-call */ 
2229
            printMainPage();
Loading history...
2229
        } else { // Otherwise, redirect to the getuser script
2230
            // Set PHP session varilables needed by the getuser script
2231
            Util::setSessionVar(
2232
                'responseurl',
2233
                (is_null($responseurl) ?
2234
                    Util::getScriptDir(true) : $responseurl)
2235
            );
2236
            Util::setSessionVar('submit', 'getuser');
2237
            Util::setSessionVar('responsesubmit', $responsesubmit);
2238
            Util::getCsrf()->setCookieAndSession();
2239
2240
            // Set up the 'header' string for redirection thru mod_shib
2241
            $mhn = static::getMachineHostname($providerId);
2242
            $redirect = "Location: https://$mhn/Shibboleth.sso/Login?target=" .
2243
                urlencode("https://$mhn/secure/getuser/");
2244
2245
            if (strlen($providerId) > 0) {
2246
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
2247
                if ($providerId == 'urn:mace:incommon:nih.gov') {
2248
                    $redirect = preg_replace(
2249
                        '%/Shibboleth.sso/Login%',
2250
                        '/Shibboleth.sso/NIHLogin',
2251
                        $redirect
2252
                    );
2253
                }
2254
2255
                $redirect .= '&providerId=' . urlencode($providerId);
2256
2257
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2258
                $forceauthn = Util::getSessionVar('forceauthn');
2259
                Util::unsetSessionVar('forceauthn');
2260
                if ($forceauthn) {
2261
                    $redirect .= '&forceAuthn=true';
2262
                } elseif (strlen($forceauthn) == 0) {
2263
                    // 'forceauth' was not set to '0' in the session, so
2264
                    // check the skin's option instead.
2265
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2266
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2267
                        $redirect .= '&forceAuthn=true';
2268
                    }
2269
                }
2270
            }
2271
2272
            $log = new Loggit();
2273
            $log->info('Shibboleth Login="' . $redirect . '"');
2274
            header($redirect);
2275
            exit; // No further processing necessary
2276
        }
2277
    }
2278
2279
    /**
2280
     * redirectToGetOAuth2User
2281
     *
2282
     * This method redirects control flow to the getuser script for
2283
     * when the user logs in via OAuth 2.0. It first checks to see
2284
     * if we have a valid session. If so, we don't need to redirect and
2285
     * instead simply show the Get Certificate page. Otherwise, we start
2286
     * an OAuth 2.0 logon by composing a parameterized GET URL using
2287
     * the OAuth 2.0 endpoint.
2288
     *
2289
     * @param string $providerId (Optional) An entityId of the
2290
     *        authenticating IdP. If not specified (or set to the empty
2291
     *        string), we check providerId PHP session variable and
2292
     *        providerId cookie (in that order) for non-empty values.
2293
     * @param string $responsesubmit (Optional) The value of the PHP session
2294
     *        'submit' variable to be set upon return from the 'getuser'
2295
     *         script.  This is utilized to control the flow of this script
2296
     *         after 'getuser'. Defaults to 'gotuser'.
2297
     */
2298
    public static function redirectToGetOAuth2User(
2299
        $providerId = '',
2300
        $responsesubmit = 'gotuser'
2301
    ) {
2302
        // If providerId not set, try the cookie value
2303
        if (strlen($providerId) == 0) {
2304
            $providerId = Util::getPortalOrCookieVar('providerId');
2305
        }
2306
2307
        // If the user has a valid 'user_uid' in the PHP session, and the
2308
        // providerId matches the 'idp' in the PHP session, then
2309
        // simply go to the 'Download Certificate' button page.
2310
        if (static::verifyCurrentUserSession($providerId)) {
2311
            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

2311
            /** @scrutinizer ignore-call */ 
2312
            printMainPage();
Loading history...
2312
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
2313
            // Set PHP session varilables needed by the getuser script
2314
            Util::unsetSessionVar('logonerror');
2315
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
2316
            Util::setSessionVar('submit', 'getuser');
2317
            Util::setSessionVar('responsesubmit', $responsesubmit);
2318
            $csrf = Util::getCsrf();
2319
            $csrf->setCookieAndSession();
2320
            $extraparams = array();
2321
            $extraparams['state'] = $csrf->getTokenValue();
2322
2323
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2324
            $forceauthn = Util::getSessionVar('forceauthn');
2325
            Util::unsetSessionVar('forceauthn');
2326
            if ($forceauthn) {
2327
                $extraparams['approval_prompt'] = 'force';
2328
            } elseif (strlen($forceauthn) == 0) {
2329
                // 'forceauth' was not set to '0' in the session, so
2330
                // check the skin's option instead.
2331
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2332
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2333
                    $extraparams['approval_prompt'] = 'force';
2334
                }
2335
            }
2336
2337
            // Get the provider name based on the provider authz URL
2338
            $providerName = Util::getAuthzIdP($providerId);
2339
2340
            // Get the authz URL and redirect
2341
            $oauth2 = new OAuth2Provider($providerName);
2342
            if (is_null($oauth2->provider)) {
2343
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
2344
                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

2344
                /** @scrutinizer ignore-call */ 
2345
                printLogonPage();
Loading history...
2345
            } else {
2346
                $authUrl = $oauth2->provider->getAuthorizationUrl(
2347
                    array_merge(
2348
                        $oauth2->authzUrlOpts,
2349
                        $extraparams
2350
                    )
2351
                );
2352
                header('Location: ' . $authUrl);
2353
                exit; // No further processing necessary
2354
            }
2355
        }
2356
    }
2357
2358
    /**
2359
     * handleGotUser
2360
     *
2361
     * This function is called upon return from one of the getuser scripts
2362
     * which should have set the 'user_uid' and 'status' PHP session variables.
2363
     * It verifies that the status return is one of STATUS_OK (even
2364
     * values).  If not, we print an error message to the user.
2365
     */
2366
    public static function handleGotUser()
2367
    {
2368
        $log = new Loggit();
2369
        $user_uid = Util::getSessionVar('user_uid');
2370
        $status = Util::getSessionVar('status');
2371
2372
        // We must get and unset session vars BEFORE any HTML output since
2373
        // a redirect may go to another site, meaning we need to update
2374
        // the session cookie before we leave the cilogon.org domain.
2375
        //
2376
        // This bit of trickery sets local variables from the PHP session
2377
        // that was just populated, using the names in the $user_attrs array.
2378
        foreach (DBService::$user_attrs as $value) {
2379
            $$value = Util::getSessionVar($value);
2380
        }
2381
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2382
        $failureuri   = Util::getSessionVar('failureuri');
2383
        $dn           = Util::getSessionVar('distinguished_name');
2384
2385
        // CIL-410 The /testidp/ flow is indicated by the presence of the
2386
        // 'storeattributes' PHP session var. In this case, simply show
2387
        // the main testidp page with user and IdP attributes.
2388
        if (!empty(Util::getSessionVar('storeattributes'))) {
2389
            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

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

2508
                /** @scrutinizer ignore-call */ 
2509
                printLogonPage();
Loading history...
2509
            } else { // Got user successfully
2510
                static::gotUserSuccess();
2511
            }
2512
        }
2513
    }
2514
2515
    /**
2516
     * gotUserSuccess
2517
     *
2518
     * This function is called after the user has been successfully
2519
     * authenticated. If the 'status' session variable is STATUS_OK
2520
     * then it checks if we have a new or changed user and logs
2521
     * that appropriately. It then continues to the MainPage.
2522
     */
2523
    public static function gotUserSuccess()
2524
    {
2525
        $log = new Loggit();
2526
        $status = Util::getSessionVar('status');
2527
2528
        // If this is the first time the user has used the CILogon Service,
2529
        // and the flow is OAuth-based, send an alert if the name contains
2530
        // any HTML entities.
2531
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2532
        $callbackuri = Util::getSessionVar('callbackuri');
2533
2534
        // Log new users with possibly empty distinguished_name values
2535
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
2536
            $dn = Util::getSessionVar('distinguished_name');
2537
            $log->info('New User' . ((strlen($dn) == 0) ? ' without a distinguished_name.' : '.'));
2538
            // If HTML entities are in the distinguished_name, send an alert.
2539
            if (
2540
                (strlen($dn) > 0) &&
2541
                ((strlen($callbackuri) > 0) ||
2542
                 (isset($clientparams['code'])))
2543
            ) {
2544
                $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
2545
                $htmldn = Util::htmlent($dn);
2546
                if (strcmp($dn, $htmldn) != 0) {
2547
                    Util::sendErrorAlert(
2548
                        'New user DN contains HTML entities',
2549
                        "htmlentites(DN) = $htmldn\n"
2550
                    );
2551
                }
2552
            }
2553
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
2554
            $log->info('User IdP attributes changed.');
2555
        }
2556
        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

2556
        /** @scrutinizer ignore-call */ 
2557
        printMainPage();
Loading history...
2557
    }
2558
2559
    /**
2560
     * generateP12
2561
     *
2562
     * This function is called when the user clicks the 'Get New
2563
     * Certificate' button. It first reads in the password fields and
2564
     * verifies that they are valid (i.e. they are long enough and match).
2565
     * Then it gets a credential from the MyProxy server and converts that
2566
     * certificate into a PKCS12 which is written to disk.  If everything
2567
     * succeeds, the temporary pkcs12 directory and lifetime is saved to
2568
     * the 'p12' PHP session variable, which is read later when the Main
2569
     * Page HTML is shown.
2570
     */
2571
    public static function generateP12()
2572
    {
2573
        $log = new Loggit();
2574
2575
        // Get the entered p12lifetime and p12multiplier and set the cookies
2576
        list($minlifetime, $maxlifetime) =
2577
            Util::getMinMaxLifetimes('pkcs12', 9516);
2578
        $p12lifetime   = Util::getPostVar('p12lifetime');
2579
        $p12multiplier = Util::getPostVar('p12multiplier');
2580
        if (strlen($p12multiplier) == 0) {
2581
            $p12multiplier = 1;  // For ECP, p12lifetime is in hours
2582
        }
2583
        $lifetime = $p12lifetime * $p12multiplier;
2584
        if ($lifetime <= 0) { // In case user entered negative number
2585
            $lifetime = $maxlifetime;
2586
            $p12lifetime = $maxlifetime;
2587
            $p12multiplier = 1;  // maxlifetime is in hours
2588
        } elseif ($lifetime < $minlifetime) {
2589
            $lifetime = $minlifetime;
2590
            $p12lifetime = $minlifetime;
2591
            $p12multiplier = 1;  // minlifetime is in hours
2592
        } elseif ($lifetime > $maxlifetime) {
2593
            $lifetime = $maxlifetime;
2594
            $p12lifetime = $maxlifetime;
2595
            $p12multiplier = 1;  // maxlifetime is in hours
2596
        }
2597
        Util::setCookieVar('p12lifetime', $p12lifetime);
2598
        Util::setCookieVar('p12multiplier', $p12multiplier);
2599
        Util::setSessionVar('p12lifetime', $p12lifetime);
2600
        Util::setSessionVar('p12multiplier', $p12multiplier);
2601
2602
        // Verify that the password is at least 12 characters long
2603
        $password1 = Util::getPostVar('password1');
2604
        $password2 = Util::getPostVar('password2');
2605
        $p12password = Util::getPostVar('p12password');  // For ECP clients
2606
        if (strlen($p12password) > 0) {
2607
            $password1 = $p12password;
2608
            $password2 = $p12password;
2609
        }
2610
        if (strlen($password1) < 12) {
2611
            Util::setSessionVar(
2612
                'p12error',
2613
                'Password must have at least 12 characters.'
2614
            );
2615
            return; // SHORT PASSWORD - NO FURTHER PROCESSING NEEDED!
2616
        }
2617
2618
        // Verify that the two password entry fields matched
2619
        if ($password1 != $password2) {
2620
            Util::setSessionVar('p12error', 'Passwords did not match.');
2621
            return; // MISMATCHED PASSWORDS - NO FURTHER PROCESSING NEEDED!
2622
        }
2623
2624
        $dn = Util::getSessionVar('distinguished_name');
2625
        if (strlen($dn) > 0) {
2626
            // Append extra info, such as 'skin', to be processed by MyProxy
2627
            $myproxyinfo = Util::getSessionVar('myproxyinfo');
2628
            if (strlen($myproxyinfo) > 0) {
2629
                $dn .= " $myproxyinfo";
2630
            }
2631
            // Attempt to fetch a credential from the MyProxy server
2632
            $cert = MyProxy::getMyProxyCredential(
2633
                $dn,
2634
                '',
2635
                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...
2636
                Util::getLOAPort(),
2637
                $lifetime,
2638
                MYPROXY_CLIENT_CRED,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYPROXY_CLIENT_CRED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2639
                ''
2640
            );
2641
2642
            // The 'openssl pkcs12' command is picky in that the private
2643
            // key must appear BEFORE the public certificate. But MyProxy
2644
            // returns the private key AFTER. So swap them around.
2645
            $cert2 = '';
2646
            if (
2647
                preg_match(
2648
                    '/-----BEGIN CERTIFICATE-----([^-]+)' .
2649
                    '-----END CERTIFICATE-----[^-]*' .
2650
                    '-----BEGIN RSA PRIVATE KEY-----([^-]+)' .
2651
                    '-----END RSA PRIVATE KEY-----/',
2652
                    $cert,
2653
                    $match
2654
                )
2655
            ) {
2656
                $cert2 = "-----BEGIN RSA PRIVATE KEY-----" .
2657
                         $match[2] . "-----END RSA PRIVATE KEY-----\n" .
2658
                         "-----BEGIN CERTIFICATE-----" .
2659
                         $match[1] . "-----END CERTIFICATE-----";
2660
            }
2661
2662
            if (strlen($cert2) > 0) { // Successfully got a certificate!
2663
                // Create a temporary directory in DEFAULT_PKCS12_DIR
2664
                $tdir = Util::tempDir(DEFAULT_PKCS12_DIR, '', 0770);
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_PKCS12_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2665
                $p12dir = str_replace(DEFAULT_PKCS12_DIR, '', $tdir);
2666
                $p12file = $tdir . '/usercred.p12';
2667
2668
                // Call the openssl pkcs12 program to convert certificate
2669
                exec('/bin/env ' .
2670
                     'RANDFILE=/tmp/.rnd ' .
2671
                     'CILOGON_PKCS12_PW=' . escapeshellarg($password1) . ' ' .
2672
                     '/usr/bin/openssl pkcs12 -export ' .
2673
                     '-passout env:CILOGON_PKCS12_PW ' .
2674
                     "-out $p12file " .
2675
                     '<<< ' . escapeshellarg($cert2));
2676
2677
                // Verify the usercred.p12 file was actually created
2678
                $size = @filesize($p12file);
2679
                if (($size !== false) && ($size > 0)) {
2680
                    $p12link = 'https://' . static::getMachineHostname() .
2681
                               '/pkcs12/' . $p12dir . '/usercred.p12';
2682
                    $p12 = (time() + 300) . " " . $p12link;
2683
                    Util::setSessionVar('p12', $p12);
2684
                    $log->info('Generated New User Certificate="' . $p12link . '"');
2685
                    //CIL-507 Special Log Message For XSEDE
2686
                    $email = Util::getSessionVar('email');
2687
                    $log->info("USAGE email=\"$email\" client=\"PKCS12\"");
2688
                    Util::logXSEDEUsage('PKCS12', $email);
2689
                } else { // Empty or missing usercred.p12 file - shouldn't happen!
2690
                    Util::setSessionVar(
2691
                        'p12error',
2692
                        'Error creating certificate. Please try again.'
2693
                    );
2694
                    Util::deleteDir($tdir); // Remove the temporary directory
2695
                    $log->info('Error creating certificate - missing usercred.p12');
2696
                }
2697
            } else { // The myproxy-logon command failed - shouldn't happen!
2698
                Util::setSessionVar(
2699
                    'p12error',
2700
                    'Error! MyProxy unable to create certificate.'
2701
                );
2702
                $log->info('Error creating certificate - myproxy-logon failed');
2703
            }
2704
        } else { // Couldn't find the 'distinguished_name' PHP session value
2705
            Util::setSessionVar(
2706
                'p12error',
2707
                'Cannot create certificate due to missing attributes.'
2708
            );
2709
            $log->info('Error creating certificate - missing dn session variable');
2710
        }
2711
    }
2712
2713
    /**
2714
     * getLogOnButtonText
2715
     *
2716
     * This function checks the current skin to see if <logonbuttontext>
2717
     * has been configured.  If so, it returns that value.  Otherwise,
2718
     * it returns 'Log On'.
2719
     *
2720
     * @return string The text of the 'Log On' button for the WAYF, as
2721
     *         configured for the skin.  Defaults to 'Log On'.
2722
     */
2723
    public static function getLogOnButtonText()
2724
    {
2725
        $retval = 'Log On';
2726
        $lobt = Util::getSkin()->getConfigOption('logonbuttontext');
2727
        if (!is_null($lobt)) {
2728
            $retval = (string)$lobt;
2729
        }
2730
        return $retval;
2731
    }
2732
2733
    /**
2734
     * reformatDN
2735
     *
2736
     * This function takes in a certificate subject DN with the email=...
2737
     * part already removed. It checks the skin to see if <dnformat> has
2738
     * been set. If so, it reformats the DN appropriately.
2739
     *
2740
     * @param string $dn The certificate subject DN (without the email=... part)
2741
     * @return string The certificate subject DN transformed according to
2742
     *         the value of the <dnformat> skin config option.
2743
     */
2744
    public static function reformatDN($dn)
2745
    {
2746
        $newdn = $dn;
2747
        $dnformat = (string)Util::getSkin()->getConfigOption('dnformat');
2748
        if (strlen($dnformat) > 0) {
2749
            if (
2750
                ($dnformat == 'rfc2253') &&
2751
                (preg_match(
2752
                    '%/DC=(.*)/DC=(.*)/C=(.*)/O=(.*)/CN=(.*)%',
2753
                    $dn,
2754
                    $match
2755
                ))
2756
            ) {
2757
                array_shift($match);
2758
                $m = array_reverse(Net_LDAP2_Util::escape_dn_value($match));
2759
                $newdn = "CN=$m[0],O=$m[1],C=$m[2],DC=$m[3],DC=$m[4]";
2760
            }
2761
        }
2762
        return $newdn;
2763
    }
2764
2765
    /**
2766
     * getMachineHostname
2767
     *
2768
     * This function is utilized in the formation of the URL for the
2769
     * PKCS12 credential download link and for the Shibboleth Single Sign-on
2770
     * session initiator URL. It returns a host-specific URL
2771
     * hostname by mapping the local machine hostname (as returned
2772
     * by 'uname -n') to an InCommon metadata cilogon.org hostname
2773
     * (e.g., polo2.cilogon.org). This function uses the HOSTNAME_ARRAY
2774
     * where the keys are the local machine hostname and
2775
     * the values are the external facing *.cilogon.org hostname.
2776
     * In case the local machine hostname cannot be found in the
2777
     * HOSTNAME_ARRAY, DEFAULT_HOSTNAME is returned.
2778
     *
2779
     * @param string $idp The entityID of the IdP used for potential
2780
     *        special handling (e.g., for Syngenta).
2781
     * @return string The full cilogon-specific hostname of this host.
2782
     */
2783
    public static function getMachineHostname($idp = '')
2784
    {
2785
        $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...
2786
        // CIL-439 For Syngenta, use just a single 'hostname' value to
2787
        // match their Active Directory configuration for CILogon's
2788
        // assertionConsumerService URL. Otherwise, map the local
2789
        // hostname to a *.cilogon.org domain name.
2790
        if ($idp != 'https://sts.windows.net/06219a4a-a835-44d5-afaf-3926343bfb89/') {
2791
            $localhost = php_uname('n');
2792
            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...
2793
                $retval = HOSTNAME_ARRAY[$localhost];
2794
            }
2795
        }
2796
        return $retval;
2797
    }
2798
2799
    /**
2800
     * getCompositeIdPList
2801
     *
2802
     * This function generates a list of IdPs to display in the 'Select
2803
     * An Identity Provider' box on the main CILogon page or on the
2804
     * TestIdP page. For the main CILogon page, this is a filtered list of
2805
     * IdPs based on the skin's greenlit/redlit list and the global
2806
     * redlit list. For the TestIdP page, the list is all InCommon IdPs.
2807
     *
2808
     * @return array A two-dimensional array where the primary key is the
2809
     *         entityID and the secondary key is either 'Display_Name'
2810
     *         or 'Organization_Name'.
2811
     */
2812
    public static function getCompositeIdPList()
2813
    {
2814
        $retarray = array();
2815
2816
        $idplist = Util::getIdpList();
2817
        $skin = Util::getSkin();
2818
2819
        // Check if the skin's config.xml has set the
2820
        // 'registeredbyincommonidps' option, which restricts the SAML-
2821
        // based IdPs to those with the <Registered_By_InCommon> tag.
2822
        // Otherwise, just get all SAML-based IdPs that have not been
2823
        // restricted based on the global 'redlit' list.
2824
        $registeredbyincommonidps = $skin->getConfigOption('registeredbyincommonidps');
2825
        if (
2826
            (!is_null($registeredbyincommonidps)) &&
2827
            ((int)$registeredbyincommonidps == 1)
2828
        ) {
2829
            $retarray = $idplist->getRegisteredByInCommonIdPs();
2830
        } else {
2831
            $retarray = $idplist->getSAMLIdPs();
2832
        }
2833
2834
        // Add all OAuth2 IdPs to the list
2835
        foreach (Util::$oauth2idps as $value) {
2836
            // CIL-617 Show OAuth2 IdPs only if client_id is configured
2837
            $client_id = constant(strtoupper($value) . '_OAUTH2_CLIENT_ID');
2838
            if (!empty($client_id)) {
2839
                $retarray[Util::getAuthzUrl($value)]['Organization_Name'] =
2840
                    $value;
2841
                $retarray[Util::getAuthzUrl($value)]['Display_Name'] =
2842
                    $value;
2843
            }
2844
        }
2845
2846
        // Check to see if the skin's config.xml has a greenlit list of IDPs.
2847
        // If so, go thru master IdP list and keep only those IdPs.
2848
        if ($skin->hasGreenlitIdps()) {
2849
            foreach ($retarray as $entityId => $names) {
2850
                if (!$skin->idpGreenlit($entityId)) {
2851
                    unset($retarray[$entityId]);
2852
                }
2853
            }
2854
        }
2855
        // Next, check to see if the skin's config.xml has a redlit list of
2856
        // IdPs. If so, cull down the master IdP list removing 'bad' IdPs.
2857
        if ($skin->hasRedlitIdps()) {
2858
            $idpredlit = $skin->getConfigOption('idpredlit');
2859
            foreach ($idpredlit->idp as $redidp) {
2860
                unset($retarray[(string)$redidp]);
2861
            }
2862
        }
2863
2864
        // Fix for CIL-174 - As suggested by Keith Hazelton, replace commas and
2865
        // hyphens with just commas.
2866
        $regex = '/(University of California)\s*[,-]\s*/';
2867
        foreach ($retarray as $entityId => $names) {
2868
            if (preg_match($regex, $names['Organization_Name'])) {
2869
                $retarray[$entityId]['Organization_Name'] =
2870
                    preg_replace($regex, '$1, ', $names['Organization_Name']);
2871
            }
2872
            if (preg_match($regex, $names['Display_Name'])) {
2873
                $retarray[$entityId]['Display_Name'] =
2874
                    preg_replace($regex, '$1, ', $names['Display_Name']);
2875
            }
2876
        }
2877
2878
        // Re-sort the retarray by Display_Name for correct alphabetization.
2879
        uasort($retarray, function ($a, $b) {
2880
            return strcasecmp(
2881
                $a['Display_Name'],
2882
                $b['Display_Name']
2883
            );
2884
        });
2885
2886
        return $retarray;
2887
    }
2888
2889
    /**
2890
     * getIdphintList
2891
     *
2892
     * This function adds support for AARC-G049 "IdP Hinting". It
2893
     * searches both the GET query parameters and the OIDC client
2894
     * parameters passed to the 'authorize' endpoint for a parameter
2895
     * named either 'selected_idp' or 'idphint'. This parameter can be
2896
     * a single entityId or a comma-separated list of entityIds.
2897
     * The entries in the list are processed to remove any 'chained'
2898
     * idphints and also to transform OIDC 'issuer' values into
2899
     * CILogon-specific 'entityIds' as used in the 'Select an IdP'
2900
     * list. Any idps which are not in the current skin's 'Select
2901
     * an IdP' list are removed. The resulting processed list of
2902
     * entityIds is returned, which may be an empty array.
2903
     *
2904
     * @param array $idps (Optional) A list of valid (i.e., greenlit) IdPs.
2905
     *        If this list is empty, then use the current skin's IdP list.
2906
     * @return array A list of entityIds / OIDC provider URLs extracted from
2907
     *         a passed-in parameter 'selected_idp' or 'idphint'. This array
2908
     *         may be empty if no such parameter was found, or if the
2909
     *         entityIds in the list were not valid.
2910
     */
2911
    public static function getIdphintList($idps = [])
2912
    {
2913
        // Check for either 'selected_idp' or 'idphint' parameter that was
2914
        // passed in via a query parameter, either for an OAuth transaction
2915
        // or just 'normally'. Note that if both 'selected_idp' and
2916
        // 'idphint' were passed, 'idphint' takes priority.
2917
2918
        $hintarray = array();
2919
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2920
2921
        $hintstr = '';
2922
        if (!empty(@$clientparams['idphint'])) {
2923
            $hintstr = $clientparams['idphint'];
2924
        } elseif (!empty(Util::getGetVar('idphint'))) {
2925
            $hintstr = Util::getGetVar('idphint');
2926
        } elseif (!empty(@$clientparams['selected_idp'])) {
2927
            $hintstr = $clientparams['selected_idp'];
2928
        } elseif (!empty(Util::getGetVar('selected_idp'))) {
2929
            $hintstr = Util::getGetVar('selected_idp');
2930
        }
2931
2932
        if (!empty($hintstr)) {
2933
            // Split on comma to account for multiple idps
2934
            $hintarray = explode(',', $hintstr);
2935
2936
            // Process the list of IdPs to transform them appropriately.
2937
            foreach ($hintarray as &$value) {
2938
                // Check for 'chained' idp hints, and remove the GET params.
2939
                if (preg_match('%([^\?]*)\?%', $value, $matches)) {
2940
                    $value = $matches[1];
2941
                }
2942
                $value = static::normalizeOAuth2IdP($value);
2943
            }
2944
            unset($value); // Break the reference with the last element.
2945
2946
            // Remove any non-greenlit IdPs from the hintarray.
2947
            if (empty($idps)) {
2948
                $idps = static::getCompositeIdPList();
2949
            }
2950
            foreach ($hintarray as $value) {
2951
                if (!isset($idps[$value])) {
2952
                    if (($key = array_search($value, $hintarray)) !== false) {
2953
                        unset($hintarray[$key]);
2954
                    }
2955
                }
2956
            }
2957
        }
2958
        return $hintarray;
2959
    }
2960
2961
    /**
2962
     * normalizeOAuth2IdP
2963
     *
2964
     * This function takes in a URL for one of the CILogon-supported OAuth2
2965
     * issuers (i.e., Google, GitHub, ORCID) and transforms it into a URL
2966
     * used by CILogon as shown in the 'Select an Identity Provider' list.
2967
     *
2968
     * @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...
2969
     *        OAuth2 IdPs supported by CILogon.
2970
     * @return string The input string transformed to a URL to be used in
2971
     *         the 'Select an Identity Provider' list. If the incoming
2972
     *         string does not match one of the OAuth2 issuers, the string
2973
     *         is returned unmodified.
2974
     */
2975
    public static function normalizeOAuth2IdP($idp)
2976
    {
2977
        if (preg_match('%^https?://accounts.google.com%', $idp)) {
2978
            $idp = 'https://accounts.google.com/o/oauth2/auth';
2979
        } elseif (preg_match('%^https?://github.com%', $idp)) {
2980
            $idp = 'https://github.com/login/oauth/authorize';
2981
        } elseif (preg_match('%^https?://orcid.org%', $idp)) {
2982
            $idp = 'https://orcid.org/oauth/authorize';
2983
        }
2984
        return $idp;
2985
    }
2986
2987
    /**
2988
     * printOIDCConsent
2989
     *
2990
     * This function prints out the block showing the scopes requested by the
2991
     * OIDC client. If 'user_code' is present in the $clientparams array,
2992
     * the Device Code is also printed so the user can verify that the code
2993
     * matches the one on the device.
2994
     */
2995
    public static function printOIDCConsent()
2996
    {
2997
        // Look in the 'scope' OIDC parameter to see which attributes are
2998
        // being requested. The values we care about are 'email', 'profile'
2999
        // (for first/last name), and 'edu.uiuc.ncsa.myproxy.getcert'
3000
        // (which gives a certificate containing first/last name AND email).
3001
        // Anything else should just be output as-is.
3002
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
3003
        $scopes = preg_split("/[\s\+]+/", $clientparams['scope']);
3004
        $scopes = array_unique($scopes); // Remove any duplicates
3005
3006
        // CIL-779 Show only those scopes which have been registered, i.e.,
3007
        // compute the set intersection of requested and registered scopes.
3008
        $client_scopes = json_decode($clientparams['client_scopes'], true);
3009
        if (!is_null($client_scopes)) {
3010
            $scopes = array_intersect($scopes, $client_scopes);
3011
        }
3012
3013
        static::printCollapseBegin('oidcconsent', 'Consent to Attribute Release', false);
3014
3015
        echo '
3016
            <div class="card-body px-5">
3017
              <div class="card-text my-2">
3018
                <a target="_blank" href="' ,
3019
                htmlspecialchars($clientparams['client_home_url']) , '">',
3020
                htmlspecialchars($clientparams['client_name']) , '</a>' ,
3021
                ' requests access to the following information.
3022
                If you do not approve this request, do not proceed.
3023
              </div> <!-- end row -->
3024
              <ul>
3025
        ';
3026
3027
        if (array_key_exists('user_code', $clientparams)) {
3028
            echo '<li>User Code: <tt>' . $clientparams['user_code'] .
3029
                '</tt></li>';
3030
        }
3031
        if (in_array('openid', $scopes)) {
3032
            echo '<li>Your CILogon user identifier</li>';
3033
            $scopes = array_diff($scopes, ['openid']);
3034
        }
3035
        if (
3036
            (in_array('profile', $scopes)) ||
3037
            (in_array('edu.uiuc.ncsa.myproxy.getcert', $scopes))
3038
        ) {
3039
            echo '<li>Your name</li>';
3040
            $scopes = array_diff($scopes, ['profile']);
3041
        }
3042
        if (
3043
            (in_array('email', $scopes)) ||
3044
            (in_array('edu.uiuc.ncsa.myproxy.getcert', $scopes))
3045
        ) {
3046
            echo '<li>Your email address</li>';
3047
            $scopes = array_diff($scopes, ['email']);
3048
        }
3049
        if (in_array('org.cilogon.userinfo', $scopes)) {
3050
            echo '<li>Your username and affiliation from your identity provider</li>';
3051
            $scopes = array_diff($scopes, ['org.cilogon.userinfo']);
3052
        }
3053
        if (in_array('edu.uiuc.ncsa.myproxy.getcert', $scopes)) {
3054
            echo '<li>A certificate that allows "' ,
3055
            htmlspecialchars($clientparams['client_name']) ,
3056
            '" to act on your behalf</li>';
3057
            $scopes = array_diff($scopes, ['edu.uiuc.ncsa.myproxy.getcert']);
3058
        }
3059
        // Output any remaining scopes as-is
3060
        foreach ($scopes as $value) {
3061
            echo '<li>', $value , '</li>';
3062
        }
3063
        echo '</ul>
3064
            </div> <!-- end card-body -->
3065
        ';
3066
3067
        static::printCollapseEnd();
3068
    }
3069
}
3070