Passed
Push — master ( 3bea6d...8e53e6 )
by Terrence
11:43
created

Content::printCollapseEnd()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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

1441
        /** @scrutinizer ignore-unused */ $memberof,

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

1442
        /** @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...
1443
        $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

1443
        /** @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...
1444
        $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

1444
        /** @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...
1445
        $clientparams,
0 ignored issues
show
Unused Code introduced by
The parameter $clientparams is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1446
        $redirect,
1447
        $redirectform,
1448
        $edugainandgetcert
1449
    ) {
1450
        Util::unsetAllUserSessionVars();
1451
1452
        static::printHeader('Error Logging On');
1453
        static::printCollapseBegin(
1454
            'attributeerror',
1455
            'Attribute Release Error',
1456
            false
1457
        );
1458
1459
        echo '
1460
              <div class="card-body px-5">
1461
        ';
1462
1463
        $errorboxstr = '
1464
                <div class="card-text my-2">
1465
                  There was a problem logging on. Your identity provider
1466
                  has not provided CILogon with required information.
1467
                </div> <!-- end card-text -->
1468
                <dl class="row">';
1469
1470
        $missingattrs = '';
1471
        // Show user which attributes are missing
1472
        if ((strlen($ePPN) == 0) && (strlen($ePTID) == 0)) {
1473
            $errorboxstr .= '
1474
                <dt class="col-sm-3">ePTID:</dt>
1475
                <dd class="col-sm-9">MISSING</dd>
1476
                <dt class="col-sm-3">ePPN:</dt>
1477
                <dd class="col-sm-9">MISSING</dd>';
1478
            $missingattrs .= '%0D%0A    eduPersonPrincipalName' .
1479
                             '%0D%0A    eduPersonTargetedID ';
1480
        }
1481
        if ((strlen($firstname) == 0) && (strlen($displayname) == 0)) {
1482
            $errorboxstr .= '
1483
                <dt class="col-sm-3">First Name:</dt>
1484
                <dd class="col-sm-9">MISSING</dd>';
1485
            $missingattrs .= '%0D%0A    givenName (first name)';
1486
        }
1487
        if ((strlen($lastname) == 0) && (strlen($displayname) == 0)) {
1488
            $errorboxstr .= '
1489
                <dt class="col-sm-3">Last Name:</dt>
1490
                <dd class="col-sm-9">MISSING</dd>';
1491
            $missingattrs .= '%0D%0A    sn (last name)';
1492
        }
1493
        if (
1494
            (strlen($displayname) == 0) &&
1495
            ((strlen($firstname) == 0) || (strlen($lastname) == 0))
1496
        ) {
1497
            $errorboxstr .= '
1498
                <dt class="col-sm-3">Display Name:</dt>
1499
                <dd class="col-sm-9">MISSING</dd>';
1500
            $missingattrs .= '%0D%0A    displayName';
1501
        }
1502
        $emailvalid = filter_var($emailaddr, FILTER_VALIDATE_EMAIL);
1503
        if ((strlen($emailaddr) == 0) || (!$emailvalid)) {
1504
            $errorboxstr .= '
1505
                <dt class="col-sm-3">Email Address:</dt>
1506
                <dd class="col-sm-9">' .
1507
            ((strlen($emailaddr) == 0) ? 'MISSING' : 'INVALID') . '</dd>';
1508
            $missingattrs .= '%0D%0A    mail (email address)';
1509
        }
1510
        // CIL-326/CIL-539 - For eduGAIN IdPs attempting to get a cert,
1511
        // print out missing R&S and SIRTFI values
1512
        $idplist = Util::getIdpList();
1513
        if ($edugainandgetcert) {
1514
            if (!$idplist->isREFEDSRandS($idp)) {
1515
                $errorboxstr .= '
1516
                    <dt class="col-sm-3"><a target="_blank"
1517
                    href="http://refeds.org/category/research-and-scholarship">Research
1518
                    and Scholarship</a>:</dt>
1519
                    <dd class="col-sm-9">MISSING</dd>';
1520
                $missingattrs .= '%0D%0A    http://refeds.org/category/research-and-scholarship';
1521
            }
1522
            if (!$idplist->isSIRTFI($idp)) {
1523
                $errorboxstr .= '
1524
                    <dt class="col-sm-3"><a target="_blank"
1525
                    href="https://refeds.org/sirtfi">SIRTFI</a>:</dt>
1526
                    <dd class="col-sm-9">MISSING</dd>';
1527
                $missingattrs .= '%0D%0A    http://refeds.org/sirtfi';
1528
            }
1529
        }
1530
        $student = false;
1531
        $errorboxstr .= '</dl>';
1532
1533
        static::printErrorBox($errorboxstr);
1534
1535
        if (
1536
            (strlen($emailaddr) == 0) &&
1537
            (preg_match('/student@/', $affiliation))
1538
        ) {
1539
            $student = true;
1540
            echo '
1541
                <div class="card-text my-2">
1542
                  <strong>If you are a student</strong>
1543
                  you may need to ask your identity provider
1544
                  to release your email address.
1545
                </div> <!-- end card-text -->
1546
            ';
1547
        }
1548
1549
        // Get contacts from metadata for email addresses
1550
        $shibarray = $idplist->getShibInfo($idp);
1551
        $emailmsg = '?subject=Attribute Release Problem for CILogon' .
1552
        '&[email protected]' .
1553
        '&body=Hello, I am having trouble logging on to ' .
1554
        'https://' . DEFAULT_HOSTNAME . '/ using the ' . $idpname .
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...
1555
        ' Identity Provider (IdP) ' .
1556
        'due to the following missing attributes:%0D%0A' .
1557
        $missingattrs;
1558
        if ($student) {
1559
            $emailmsg .= '%0D%0A%0D%0ANote that my account is ' .
1560
            'marked "student" and thus my email address may need ' .
1561
            'to be released.';
1562
        }
1563
        $emailmsg .= '%0D%0A%0D%0APlease see ' .
1564
            'http://www.cilogon.org/service/addidp for more ' .
1565
            'details. Thank you for any help you can provide.';
1566
        echo '
1567
                <div class="card-text my-2">
1568
                  Contact your identity provider to let them know you are
1569
                  having having a problem logging on to CILogon.
1570
                </div> <!-- end card-text -->
1571
                <ul>
1572
            ';
1573
1574
        $addrfound = false;
1575
        $name = @$shibarray['Support Name'];
1576
        $addr = @$shibarray['Support Address'];
1577
        $addr = preg_replace('/^mailto:/', '', $addr);
1578
1579
        if (strlen($addr) > 0) {
1580
            $addrfound = true;
1581
            if (strlen($name) == 0) { // Use address if no name given
1582
                $name = $addr;
1583
            }
1584
            echo '
1585
                  <li> Support Contact: ' ,
1586
                  $name , ' <a class="btn btn-primary" href="mailto:' ,
1587
                  $addr , $emailmsg , '">' ,
1588
                  $addr , '</a>
1589
                  </li>';
1590
        }
1591
1592
        if (!$addrfound) {
1593
            $name = @$shibarray['Technical Name'];
1594
            $addr = @$shibarray['Technical Address'];
1595
            $addr = preg_replace('/^mailto:/', '', $addr);
1596
            if (strlen($addr) > 0) {
1597
                $addrfound = true;
1598
                if (strlen($name) == 0) { // Use address if no name given
1599
                    $name = $addr;
1600
                }
1601
                echo '
1602
                      <li> Technical Contact: ' ,
1603
                      $name , ' <a class="btn btn-primary" href="mailto:' ,
1604
                      $addr , $emailmsg , '">' ,
1605
                      $addr , '</a>
1606
                      </li>';
1607
            }
1608
        }
1609
1610
        if (!$addrfound) {
1611
            $name = @$shibarray['Administrative Name'];
1612
            $addr = @$shibarray['Administrative Address'];
1613
            $addr = preg_replace('/^mailto:/', '', $addr);
1614
            if (strlen($addr) > 0) {
1615
                if (strlen($name) == 0) { // Use address if no name given
1616
                    $name = $addr;
1617
                }
1618
                echo '
1619
                      <li>Administrative Contact: ' ,
1620
                      $name , ' <a class="btn btn-primary" href="mailto:' ,
1621
                      $addr , $emailmsg , '">' ,
1622
                      $addr , '</a>
1623
                      </li>';
1624
            }
1625
        }
1626
1627
        echo '
1628
                </ul>
1629
                <div class="card-text my-2">
1630
                  Alternatively, you can contact us at the email address
1631
                  at the bottom of the page.
1632
                </div> <!-- end card-text -->
1633
            ';
1634
1635
        static::printFormHead($redirect, 'get');
1636
1637
        echo '
1638
              <div class="card-text my-2">
1639
                <div class="form-group">
1640
                  <div class="form-row align-items-center
1641
                  justify-content-center">
1642
                    <div class="col-auto">
1643
                      ', $redirectform, '
1644
                      <input type="submit" name="submit"
1645
                      class="btn btn-primary submit form-control"
1646
                      value="Proceed" />
1647
                    </div>
1648
                  </div> <!-- end form-row align-items-center -->
1649
                </div> <!-- end form-group -->
1650
              </div> <!-- end card-text -->
1651
            </form>
1652
            </div> <!-- end card-body -->';
1653
1654
        Content::printCollapseEnd();
1655
        Content::printFooter();
1656
    }
1657
1658
    /**
1659
     * printOAuth2AttributeReleaseErrorPage
1660
     *
1661
     * This function is called by handleGotUser when the IdP did not release
1662
     * all required attributes for the user. In the case of the OAuth2
1663
     * providers, this is typically due to one of first name, last name,
1664
     * and/or email address. Print out a special message for each OAuth2 IdP
1665
     * to let the user know how to fix the issue.
1666
     *
1667
     * @param string $idpname The name of the OAuth2 IdP.
1668
     * @param string $redirect The url for the <form> element
1669
     * @param string $redirectform Additional hidden input fields for the
1670
     *        <form>.
1671
     *
1672
     */
1673
    public static function printOAuth2AttributeReleaseErrorPage($idpname, $redirect, $redirectform)
1674
    {
1675
        Util::unsetAllUserSessionVars();
1676
        static::printHeader('Error Logging On');
1677
        static::printCollapseBegin(
1678
            'oauth2attrerror',
1679
            'Error Logging On',
1680
            false
1681
        );
1682
1683
        echo '
1684
            <div class="card-body px-5">';
1685
1686
        static::printErrorBox('There was a problem logging on.');
1687
1688
        if ($idpname == 'Google') {
1689
            echo '
1690
              <div class="card-text my-2">
1691
                There was a problem logging on. It appears that you have
1692
                attempted to use Google as your identity provider, but your
1693
                name or email address was missing. To rectify this problem,
1694
                go to the <a target="_blank"
1695
                href="https://myaccount.google.com/privacy#personalinfo">Google
1696
                Account Personal Information page</a>, and enter your first
1697
                name, last name, and email address. (All other Google
1698
                account information is not required by the CILogon Service.)
1699
              </div>
1700
              <div class="card-text my-2">
1701
                After you have updated your Google account profile, click
1702
                the "Proceed" button below and attempt to log on
1703
                with your Google account again. If you have any questions,
1704
                please contact us at the email address at the bottom of the
1705
                page.
1706
              </div>';
1707
        } elseif ($idpname == 'GitHub') {
1708
            echo '
1709
              <div class="card-text my-2">
1710
                There was a problem logging on. It appears that you have
1711
                attempted to use GitHub as your identity provider, but your
1712
                name or email address was missing. To rectify this problem,
1713
                go to the <a target="_blank"
1714
                href="https://github.com/settings/profile">GitHub
1715
                Public Profile page</a>, and enter your name and email
1716
                address. (All other GitHub account information is not
1717
                required by the CILogon Service.)
1718
              </div>
1719
              <div class="card-text my-2">
1720
                After you have updated your GitHub account profile, click
1721
                the "Proceed" button below and attempt to log on
1722
                with your GitHub account again. If you have any questions,
1723
                please contact us at the email address at the bottom of the
1724
                page.
1725
              </div>';
1726
        } elseif ($idpname == 'ORCID') {
1727
            echo '
1728
              <div class="card-text my-2">
1729
                There was a problem logging on. It appears that you have
1730
                attempted to use ORCID as your identity provider, but your
1731
                name or email address was missing. To rectify this problem,
1732
                go to your <a target="_blank"
1733
                href="https://orcid.org/my-orcid">ORCID
1734
                Profile page</a>, enter your name and email address, and
1735
                make sure they can be viewed by Everyone.
1736
                (All other ORCID account information is not required by
1737
                the CILogon Service.)
1738
              </div>
1739
              <div class="card-text my-2">
1740
                After you have updated your ORCID account profile, click
1741
                the "Proceed" button below and attempt to log on
1742
                with your ORCID account again. If you have any questions,
1743
                please contact us at the email address at the bottom of the
1744
                page.
1745
              </div>';
1746
        }
1747
1748
        static::printFormHead($redirect, 'get');
1749
1750
        echo '
1751
              <div class="card-text my-2">
1752
                <div class="form-group">
1753
                  <div class="form-row align-items-center
1754
                  justify-content-center">
1755
                    <div class="col-auto">
1756
                      <input type="hidden" name="providerId"
1757
                      value="' ,
1758
                      Util::getAuthzUrl($idpname) , '" />
1759
                      ', $redirectform, '
1760
                      <input type="submit" name="submit"
1761
                      class="btn btn-primary submit form-control"
1762
                      value="Proceed" />
1763
                    </div>
1764
                  </div> <!-- end form-row align-items-center -->
1765
                </div> <!-- end form-group -->
1766
              </div> <!-- end card-text -->
1767
            </form>
1768
            </div> <!-- end card-body -->';
1769
1770
        Content::printCollapseEnd();
1771
        Content::printFooter();
1772
    }
1773
1774
    /**
1775
     * handleLogOnButtonClicked
1776
     *
1777
     * This function is called when the user clicks the 'Log On' button
1778
     * on the IdP selection page. It checks to see if the 'Remember this
1779
     * selection' checkbox was checked and sets a cookie appropriately. It
1780
     * also sets a cookie 'providerId' so the last chosen IdP will be
1781
     * selected the next time the user visits the site. The function then
1782
     * calls the appropriate 'redirectTo...' function to send the user
1783
     * to the chosen IdP.
1784
     */
1785
    public static function handleLogOnButtonClicked()
1786
    {
1787
        // Get the list of currently available IdPs
1788
        $idps = static::getCompositeIdPList();
1789
1790
        // Set the cookie for keepidp if the checkbox was checked
1791
        $pc = new PortalCookie();
1792
        $pn = $pc->getPortalName();
0 ignored issues
show
Unused Code introduced by
The assignment to $pn is dead and can be removed.
Loading history...
1793
        Util::setPortalOrCookie(
1794
            $pc,
1795
            'keepidp',
1796
            ((strlen(Util::getPostVar('keepidp')) > 0) ? 'checked' : '')
1797
        );
1798
1799
        // Get the user-chosen IdP from the posted form
1800
        $providerId = Util::getPostVar('providerId');
1801
        $providerIdValid = ((strlen($providerId) > 0) &&
1802
                            (isset($idps[$providerId])));
1803
1804
        // Set the cookie for the last chosen IdP and redirect to it if in list
1805
        Util::setPortalOrCookie(
1806
            $pc,
1807
            'providerId',
1808
            ($providerIdValid ? $providerId : ''),
1809
            true
1810
        );
1811
        if ($providerIdValid) {
1812
            $providerName = Util::getAuthzIdP($providerId);
1813
            if (in_array($providerName, Util::$oauth2idps)) {
1814
                // Log in with an OAuth2 IdP
1815
                static::redirectToGetOAuth2User($providerId);
1816
            } else { // Use InCommon authn
1817
                static::redirectToGetShibUser($providerId);
1818
            }
1819
        } else { // IdP not in list, or no IdP selected
1820
            Util::setSessionVar('logonerror', 'Please select a valid IdP.');
1821
            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

1821
            /** @scrutinizer ignore-call */ 
1822
            printLogonPage();
Loading history...
1822
        }
1823
    }
1824
1825
    /**
1826
     * handleNoSubmitButtonClicked
1827
     *
1828
     * This function is the 'default' case when no 'submit' button has been
1829
     * clicked, or if the submit session variable is not set. It checks
1830
     * to see if either the <forceinitialidp> option is set, or if the
1831
     * 'Remember this selection' checkbox was previously checked. If so,
1832
     * then rediret to the appropriate IdP. Otherwise, print the main
1833
     * Log On page.
1834
     */
1835
    public static function handleNoSubmitButtonClicked()
1836
    {
1837
        $providerId = '';
1838
        $keepidp = '';
1839
        $selected_idp = '';
1840
        $redirect_uri = '';
1841
        $client_id = '';
1842
        $callbackuri = Util::getSessionVar('callbackuri');
1843
        $readidpcookies = true;  // Assume config options are not set
1844
        $skin = Util::getSkin();
1845
        $forceinitialidp = (int)$skin->getConfigOption('forceinitialidp');
1846
        $initialidp = (string)$skin->getConfigOption('initialidp');
1847
1848
        // If this is a OIDC transaction, get the redirect_uri and
1849
        // client_id parameters from the session var clientparams.
1850
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
1851
        if (isset($clientparams['redirect_uri'])) {
1852
            $redirect_uri = $clientparams['redirect_uri'];
1853
        }
1854
        if (isset($clientparams['client_id'])) {
1855
            $client_id = $clientparams['client_id'];
1856
        }
1857
1858
        // Use the first element of the idphint list as the selected_idp.
1859
        $idphintlist = static::getIdphintList();
1860
        if (!empty($idphintlist)) {
1861
            $selected_idp = $idphintlist[0];
1862
        }
1863
1864
        if ((strlen($redirect_uri) > 0) || (strlen($client_id) > 0)) {
1865
            // CIL-431 - If the OAuth2/OIDC $redirect_uri or $client_id is set,
1866
            // then check for a match in the BYPASS_IDP_ARRAY to see if we
1867
            // should automatically redirect to a specific IdP. Used mainly
1868
            // by campus gateways.
1869
            $bypassidp = '';
1870
            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...
1871
                if (
1872
                    (preg_match($key, $redirect_uri)) ||
1873
                    (preg_match($key, $client_id))
1874
                ) {
1875
                    $bypassidp = $value;
1876
                    break;
1877
                }
1878
            }
1879
1880
            // CIL-613 - Next, check for a match in the ALLOW_BYPASS_ARRAY.
1881
            // If found, then allow the idphint/selected_idp to be used as the
1882
            // IdP to redirect to.
1883
            if (empty($bypassidp) && (!empty($selected_idp))) {
1884
                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...
1885
                    if (
1886
                        (preg_match($value, $redirect_uri)) ||
1887
                        (preg_match($value, $client_id))
1888
                    ) {
1889
                        $bypassidp = $selected_idp;
1890
                        break;
1891
                    }
1892
                }
1893
            }
1894
1895
            if (!empty($bypassidp)) { // Match found!
1896
                $providerId = $bypassidp;
1897
                $keepidp = 'checked';
1898
                // To skip the next code blocks, unset a few variables.
1899
                $forceinitialidp = 0;     // Skip checking this option
1900
                $selected_idp = '';       // Skip any passed-in option
1901
                $readidpcookies = false;  // Don't read in the IdP cookies
1902
            }
1903
        }
1904
1905
        // If the <forceinitialidp> option is set, use either the
1906
        // <initialidp> or the selected_idp as the providerId, and use
1907
        // <forceinitialidp> as keepIdp. Otherwise, read the cookies
1908
        // 'providerId' and 'keepidp'.
1909
        if (
1910
            ($forceinitialidp == 1) &&
1911
            ((strlen($initialidp) > 0) || (strlen($selected_idp) > 0))
1912
        ) {
1913
            // If the <allowforceinitialidp> option is set, then make sure
1914
            // the callback / redirect uri is in the portal list.
1915
            $afii = $skin->getConfigOption('portallistaction', 'allowforceinitialidp');
1916
            if (
1917
                (is_null($afii)) || // Option not set, no need to check portal list
1918
                (((int)$afii == 1) &&
1919
                  (($skin->inPortalList($redirect_uri)) ||
1920
                   ($skin->inPortalList($client_id)) ||
1921
                   ($skin->inPortalList($callbackuri))))
1922
            ) {
1923
                // 'selected_idp' takes precedence over <initialidp>
1924
                if (strlen($selected_idp) > 0) {
1925
                    $providerId = $selected_idp;
1926
                } else {
1927
                    $providerId = $initialidp;
1928
                }
1929
                $keepidp = $forceinitialidp;
1930
                $readidpcookies = false; // Don't read in the IdP cookies
1931
            }
1932
        }
1933
1934
        // <initialidp> options not set, or portal not in portal list?
1935
        // Get idp and 'Remember this selection' from cookies instead.
1936
        $pc = new PortalCookie();
1937
        $pn = $pc->getPortalName();
1938
        if ($readidpcookies) {
1939
            // Check the portalcookie first, then the 'normal' cookies
1940
            if (strlen($pn) > 0) {
1941
                $keepidp    = $pc->get('keepidp');
1942
                $providerId = $pc->get('providerId');
1943
            } else {
1944
                $keepidp    = Util::getCookieVar('keepidp');
1945
                $providerId = Util::getCookieVar('providerId');
1946
            }
1947
        }
1948
1949
        // If both 'keepidp' and 'providerId' were set (and the
1950
        // providerId is a whitelisted IdP or valid OpenID provider),
1951
        // then skip the Logon page and proceed to the appropriate
1952
        // getuser script.
1953
        if ((strlen($providerId) > 0) && (strlen($keepidp) > 0)) {
1954
            // If selected_idp was specified at the OIDC authorize endpoint,
1955
            // make sure that it matches the saved providerId. If not,
1956
            // then show the Logon page and uncheck the keepidp checkbox.
1957
            if ((strlen($selected_idp) == 0) || ($selected_idp == $providerId)) {
1958
                Util::setPortalOrCookie($pc, 'providerId', $providerId, true);
1959
                $providerName = Util::getAuthzIdP($providerId);
1960
                if (in_array($providerName, Util::$oauth2idps)) {
1961
                    // Log in with an OAuth2 IdP
1962
                    static::redirectToGetOAuth2User($providerId);
1963
                } elseif (Util::getIdpList()->exists($providerId)) {
1964
                    // Log in with InCommon
1965
                    static::redirectToGetShibUser($providerId);
1966
                } else { // $providerId not in whitelist
1967
                    Util::setPortalOrCookie($pc, 'providerId', '', true);
1968
                    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

1968
                    /** @scrutinizer ignore-call */ 
1969
                    printLogonPage();
Loading history...
1969
                }
1970
            } else { // selected_idp does not match saved providerId
1971
                Util::setPortalOrCookie($pc, 'keepidp', '', true);
1972
                printLogonPage();
1973
            }
1974
        } else { // One of providerId or keepidp was not set
1975
            printLogonPage();
1976
        }
1977
    }
1978
1979
    /**
1980
     * verifyCurrentUserSession
1981
     *
1982
     * This function verifies the contents of the PHP session.  It checks
1983
     * the following:
1984
     * (1) The persistent store 'uid', the Identity Provider 'idp', the
1985
     *     IdP Display Name 'idpname', and the 'status' (of getUser()) are
1986
     *     all non-empty strings.
1987
     * (2) The 'status' (of getUser()) is even (i.e. STATUS_OK).
1988
     * (3) If $providerId is passed-in, it must match 'idp'.
1989
     * If all checks are good, then this function returns true.
1990
     *
1991
     * @param string $providerId (Optional) The user-selected Identity
1992
     *        Provider. If set, make sure $providerId matches the PHP
1993
     *        session variable 'idp'.
1994
     * @return bool True if the contents of the PHP session ar valid.
1995
     *              False otherwise.
1996
     */
1997
    public static function verifyCurrentUserSession($providerId = '')
1998
    {
1999
        $retval = false;
2000
2001
        $idp       = Util::getSessionVar('idp');
2002
        $idpname   = Util::getSessionVar('idpname');
2003
        $uid       = Util::getSessionVar('uid');
2004
        $status    = Util::getSessionVar('status');
2005
        $dn        = Util::getSessionVar('dn');
2006
        $authntime = Util::getSessionVar('authntime');
2007
2008
        // CIL-410 When using the /testidp/ flow, the 'storeattributes'
2009
        // session var is set. In this case, the only attribute that
2010
        // is needed is 'idp' (entityID).
2011
        if (Util::getSessionVar('storeattributes') == '1') {
2012
            if (strlen($idp) > 0) {
2013
                $retval = true;
2014
            }
2015
        } elseif (
2016
            (strlen($uid) > 0) && (strlen($idp) > 0) &&
2017
            (strlen($idpname) > 0) && (strlen($status) > 0) &&
2018
            (strlen($dn) > 0) && (strlen($authntime) > 0) &&
2019
            (!($status & 1)) // All STATUS_OK codes are even
2020
        ) {
2021
            // Check for eduGAIN IdP and possible get cert context
2022
            if (Util::isEduGAINAndGetCert()) {
2023
                Util::unsetUserSessionVars();
2024
            } elseif ((strlen($providerId) == 0) || ($providerId == $idp)) {
2025
                // If $providerId passed in, make sure it matches the $idp
2026
                $retval = true;
2027
                Util::getSkin()->init(); // Does the IdP need a forced skin?
2028
            }
2029
        }
2030
2031
        return $retval;
2032
    }
2033
2034
    /**
2035
     * redirectToGetShibUser
2036
     *
2037
     * This method redirects control flow to the getuser script for
2038
     * If the first parameter (a whitelisted entityId) is not specified,
2039
     * we check to see if either the providerId PHP session variable or the
2040
     * providerId cookie is set (in that order) and use one if available.
2041
     * The function then checks to see if there is a valid PHP session
2042
     * and if the providerId matches the 'idp' in the session.  If so, then
2043
     * we don't need to redirect to '/secure/getuser/' and instead we
2044
     * we display the main page.  However, if the PHP session is not valid,
2045
     * then this function redirects to the '/secure/getuser/' script so as
2046
     * to do a Shibboleth authentication via mod_shib. When the providerId
2047
     * is non-empty, the SessionInitiator will automatically go to that IdP
2048
     * (i.e. without stopping at a WAYF).  This function also sets
2049
     * several PHP session variables that are needed by the getuser script,
2050
     * including the 'responsesubmit' variable which is set as the return
2051
     * 'submit' variable in the 'getuser' script.
2052
     *
2053
     * @param string $providerId (Optional) An entityId of the
2054
     *        authenticating IdP. If not specified (or set to the empty
2055
     *        string), we check providerId PHP session variable and
2056
     *        providerId cookie (in that order) for non-empty values.
2057
     * @param string $responsesubmit (Optional) The value of the PHP session
2058
     *       'submit' variable to be set upon return from the 'getuser'
2059
     *        script.  This is utilized to control the flow of this script
2060
     *        after 'getuser'. Defaults to 'gotuser'.
2061
     * @param string $responseurl (Optional) A response url for redirection
2062
     *        after successful processing at /secure/getuser/. Defaults to
2063
     *        the current script directory.
2064
     */
2065
    public static function redirectToGetShibUser(
2066
        $providerId = '',
2067
        $responsesubmit = 'gotuser',
2068
        $responseurl = null
2069
    ) {
2070
        // If providerId not set, try the cookie value
2071
        if (strlen($providerId) == 0) {
2072
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
2073
        }
2074
2075
        // If the user has a valid 'uid' in the PHP session, and the
2076
        // providerId matches the 'idp' in the PHP session, then
2077
        // simply go to the main page.
2078
        if (static::verifyCurrentUserSession($providerId)) {
2079
            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

2079
            /** @scrutinizer ignore-call */ 
2080
            printMainPage();
Loading history...
2080
        } else { // Otherwise, redirect to the getuser script
2081
            // Set PHP session varilables needed by the getuser script
2082
            Util::setSessionVar(
2083
                'responseurl',
2084
                (is_null($responseurl) ?
2085
                    Util::getScriptDir(true) : $responseurl)
2086
            );
2087
            Util::setSessionVar('submit', 'getuser');
2088
            Util::setSessionVar('responsesubmit', $responsesubmit);
2089
            Util::getCsrf()->setCookieAndSession();
2090
2091
            // Set up the 'header' string for redirection thru mod_shib
2092
            $mhn = static::getMachineHostname($providerId);
2093
            $redirect = "Location: https://$mhn/Shibboleth.sso/Login?target=" .
2094
                urlencode("https://$mhn/secure/getuser/");
2095
2096
            if (strlen($providerId) > 0) {
2097
                // Use special NIHLogin Shibboleth SessionInitiator for acsByIndex
2098
                if ($providerId == 'urn:mace:incommon:nih.gov') {
2099
                    $redirect = preg_replace(
2100
                        '%/Shibboleth.sso/Login%',
2101
                        '/Shibboleth.sso/NIHLogin',
2102
                        $redirect
2103
                    );
2104
                }
2105
2106
                $redirect .= '&providerId=' . urlencode($providerId);
2107
2108
                // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2109
                $forceauthn = Util::getSessionVar('forceauthn');
2110
                Util::unsetSessionVar('forceauthn');
2111
                if ($forceauthn) {
2112
                    $redirect .= '&forceAuthn=true';
2113
                } elseif (strlen($forceauthn) == 0) {
2114
                    // 'forceauth' was not set to '0' in the session, so
2115
                    // check the skin's option instead.
2116
                    $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2117
                    if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2118
                        $redirect .= '&forceAuthn=true';
2119
                    }
2120
                }
2121
            }
2122
2123
            $log = new Loggit();
2124
            $log->info('Shibboleth Login="' . $redirect . '"');
2125
            header($redirect);
2126
            exit; // No further processing necessary
2127
        }
2128
    }
2129
2130
    /**
2131
     * redirectToGetOAuth2User
2132
     *
2133
     * This method redirects control flow to the getuser script for
2134
     * when the user logs in via OAuth 2.0. It first checks to see
2135
     * if we have a valid session. If so, we don't need to redirect and
2136
     * instead simply show the Get Certificate page. Otherwise, we start
2137
     * an OAuth 2.0 logon by composing a parameterized GET URL using
2138
     * the OAuth 2.0 endpoint.
2139
     *
2140
     * @param string $providerId (Optional) An entityId of the
2141
     *        authenticating IdP. If not specified (or set to the empty
2142
     *        string), we check providerId PHP session variable and
2143
     *        providerId cookie (in that order) for non-empty values.
2144
     * @param string $responsesubmit (Optional) The value of the PHP session
2145
     *        'submit' variable to be set upon return from the 'getuser'
2146
     *         script.  This is utilized to control the flow of this script
2147
     *         after 'getuser'. Defaults to 'gotuser'.
2148
     */
2149
    public static function redirectToGetOAuth2User(
2150
        $providerId = '',
2151
        $responsesubmit = 'gotuser'
2152
    ) {
2153
        // If providerId not set, try the cookie value
2154
        if (strlen($providerId) == 0) {
2155
            $providerId = Util::getPortalOrNormalCookieVar('providerId');
2156
        }
2157
2158
        // If the user has a valid 'uid' in the PHP session, and the
2159
        // providerId matches the 'idp' in the PHP session, then
2160
        // simply go to the 'Download Certificate' button page.
2161
        if (static::verifyCurrentUserSession($providerId)) {
2162
            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

2162
            /** @scrutinizer ignore-call */ 
2163
            printMainPage();
Loading history...
2163
        } else { // Otherwise, redirect to the OAuth 2.0 endpoint
2164
            // Set PHP session varilables needed by the getuser script
2165
            Util::unsetSessionVar('logonerror');
2166
            Util::setSessionVar('responseurl', Util::getScriptDir(true));
2167
            Util::setSessionVar('submit', 'getuser');
2168
            Util::setSessionVar('responsesubmit', $responsesubmit);
2169
            $csrf = Util::getCsrf();
2170
            $csrf->setCookieAndSession();
2171
            $extraparams = array();
2172
            $extraparams['state'] = $csrf->getTokenValue();
2173
2174
            // To bypass SSO at IdP, check for session var 'forceauthn' == 1
2175
            $forceauthn = Util::getSessionVar('forceauthn');
2176
            Util::unsetSessionVar('forceauthn');
2177
            if ($forceauthn) {
2178
                $extraparams['approval_prompt'] = 'force';
2179
            } elseif (strlen($forceauthn) == 0) {
2180
                // 'forceauth' was not set to '0' in the session, so
2181
                // check the skin's option instead.
2182
                $forceauthn = Util::getSkin()->getConfigOption('forceauthn');
2183
                if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
2184
                    $extraparams['approval_prompt'] = 'force';
2185
                }
2186
            }
2187
2188
            // Get the provider name based on the provider authz URL
2189
            $providerName = Util::getAuthzIdP($providerId);
2190
2191
            // Get the authz URL and redirect
2192
            $oauth2 = new OAuth2Provider($providerName);
2193
            if (is_null($oauth2->provider)) {
2194
                Util::setSessionVar('logonerror', 'Invalid Identity Provider.');
2195
                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

2195
                /** @scrutinizer ignore-call */ 
2196
                printLogonPage();
Loading history...
2196
            } else {
2197
                $authUrl = $oauth2->provider->getAuthorizationUrl(
2198
                    array_merge(
2199
                        $oauth2->authzUrlOpts,
2200
                        $extraparams
2201
                    )
2202
                );
2203
                header('Location: ' . $authUrl);
2204
                exit; // No further processing necessary
2205
            }
2206
        }
2207
    }
2208
2209
    /**
2210
     * handleGotUser
2211
     *
2212
     * This function is called upon return from one of the getuser scripts
2213
     * which should have set the 'uid' and 'status' PHP session variables.
2214
     * It verifies that the status return is one of STATUS_OK (even
2215
     * values).  If not, we print an error message to the user.
2216
     */
2217
    public static function handleGotUser()
2218
    {
2219
        $log = new Loggit();
2220
        $uid = Util::getSessionVar('uid');
2221
        $status = Util::getSessionVar('status');
2222
2223
        // We must get and unset session vars BEFORE any HTML output since
2224
        // a redirect may go to another site, meaning we need to update
2225
        // the session cookie before we leave the cilogon.org domain.
2226
        $ePPN         = Util::getSessionVar('ePPN');
2227
        $ePTID        = Util::getSessionVar('ePTID');
2228
        $firstname    = Util::getSessionVar('firstname');
2229
        $lastname     = Util::getSessionVar('lastname');
2230
        $displayname  = Util::getSessionVar('displayname');
2231
        $emailaddr    = Util::getSessionVar('emailaddr');
2232
        $idp          = Util::getSessionVar('idp');
2233
        $idpname      = Util::getSessionVar('idpname');
2234
        $affiliation  = Util::getSessionVar('affiliation');
2235
        $ou           = Util::getSessionVar('ou');
2236
        $memberof     = Util::getSessionVar('memberof');
2237
        $acr          = Util::getSessionVar('acr');
2238
        $entitlement  = Util::getSessionVar('entitlement');
2239
        $itrustuin    = Util::getSessionVar('itrustuin');
2240
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2241
        $failureuri   = Util::getSessionVar('failureuri');
2242
2243
        // CIL-410 The /testidp/ flow is indicated by the presence of the
2244
        // 'storeattributes' PHP session var. In this case, simply show
2245
        // the main testidp page with user and IdP attributes.
2246
        if (!empty(Util::getSessionVar('storeattributes'))) {
2247
            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

2247
            /** @scrutinizer ignore-call */ 
2248
            printMainPage();
Loading history...
2248
            return;
2249
        }
2250
2251
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
2252
        // If found, set 'Proceed' button redirect appropriately.
2253
        $redirect = '';
2254
        $redirectform = '';
2255
        // First, check for OIDC redirect_uri, with parameters in <form>
2256
        if (isset($clientparams['redirect_uri'])) {
2257
            $redirect = $clientparams['redirect_uri'];
2258
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
2259
                '<input type="hidden" name="error_description" value="Missing attributes" />';
2260
            if (isset($clientparams['state'])) {
2261
                $redirectform .= '<input type="hidden" name="state" value="' .
2262
                    $clientparams['state'] . '" />';
2263
            }
2264
        }
2265
2266
        // Next, check for OAuth 1.0a
2267
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
2268
            $redirect = $failureuri . "?reason=missing_attributes";
2269
        }
2270
2271
        $isEduGAINAndGetCert = Util::isEduGAINAndGetCert($idp, $idpname);
2272
2273
        // Check for various error conditions and print out appropriate page
2274
        if (
2275
            (strlen($uid) == 0) ||    // Empty uid
2276
            (strlen($status) == 0) || // Empty status
2277
            ($status & 1) ||          // Odd-numbered status = error
2278
            ($isEduGAINAndGetCert)    // Not allowed
2279
        ) {
2280
            $log->error(
2281
                'Failed to getuser' .
2282
                ($isEduGAINAndGetCert ? : ' due to eduGAIN IdP restriction') .
0 ignored issues
show
Bug introduced by
Are you sure $isEduGAINAndGetCert ?: ...duGAIN IdP restriction' of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

2282
                (/** @scrutinizer ignore-type */ $isEduGAINAndGetCert ? : ' due to eduGAIN IdP restriction') .
Loading history...
2283
                '.'
2284
            );
2285
2286
            // Is this a SAML IdP?
2287
            $idplist = Util::getIdpList();
2288
            $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
2289
2290
            // Was there a misssing parameter?
2291
            $missingparam = ($status ==
2292
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']);
2293
2294
            if (($isEduGAINAndGetCert) || ($missingparam && $samlidp)) {
2295
                static::printSAMLAttributeReleaseErrorPage(
2296
                    $ePPN,
2297
                    $ePTID,
2298
                    $firstname,
2299
                    $lastname,
2300
                    $displayname,
2301
                    $emailaddr,
2302
                    $idp,
2303
                    $idpname,
2304
                    $affiliation,
2305
                    $ou,
2306
                    $memberof,
2307
                    $acr,
2308
                    $entitlement,
2309
                    $itrustuin,
2310
                    $clientparams,
2311
                    $redirect,
2312
                    $redirectform,
2313
                    $isEduGAINAndGetCert
2314
                );
2315
            } elseif ($missingparam && (!$samlidp)) { // OAuth2 IdP
2316
                static::printOAuth2AttributeReleaseErrorPage(
2317
                    $idpname,
2318
                    $redirect,
2319
                    $redirectform
2320
                );
2321
            } else { // General error
2322
                static::printGeneralErrorPage($redirect, $redirectform);
2323
            }
2324
        } else { // EVERYTHING IS OKAY SO FAR
2325
            // Extra security check: Once the user has successfully
2326
            // authenticated with an IdP, verify that the chosen IdP was
2327
            // actually whitelisted. If not, then set error message and show
2328
            // Select an Identity Provider page again.
2329
            Util::getSkin()->init();  // Check for forced skin
2330
            $idps = static::getCompositeIdPList();
2331
            $providerId = Util::getSessionVar('idp');
2332
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
2333
                Util::setSessionVar(
2334
                    'logonerror',
2335
                    'Invalid IdP selected. Please try again.'
2336
                );
2337
                Util::sendErrorAlert(
2338
                    'Authentication attempt using non-whitelisted IdP',
2339
                    'A user successfully authenticated with an IdP,
2340
                    however, the selected IdP was not in the list of
2341
                    whitelisted IdPs as determined by the current skin. This
2342
                    might indicate the user attempted to circumvent the
2343
                    security check in "handleGotUser()" for valid IdPs for
2344
                    the skin.'
2345
                );
2346
                Util::unsetCookieVar('providerId');
2347
                Util::unsetAllUserSessionVars();
2348
                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

2348
                /** @scrutinizer ignore-call */ 
2349
                printLogonPage();
Loading history...
2349
            } else { // Got user successfully
2350
                static::gotUserSuccess();
2351
            }
2352
        }
2353
    }
2354
2355
    /**
2356
     * gotUserSuccess
2357
     *
2358
     * This function is called after the user has been successfully
2359
     * authenticated. If the 'status' session variable is STATUS_OK
2360
     * then it checks if we have a new or changed user and logs
2361
     * that appropriately. It then continues to the MainPage.
2362
     */
2363
    public static function gotUserSuccess()
2364
    {
2365
        $log = new Loggit();
2366
        $status = Util::getSessionVar('status');
2367
2368
        // If this is the first time the user has used the CILogon Service,
2369
        // and the flow is OAuth-based, send an alert if the name contains
2370
        // any HTML entities.
2371
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
2372
        $callbackuri = Util::getSessionVar('callbackuri');
2373
2374
        if (
2375
            ($status == DBService::$STATUS['STATUS_NEW_USER']) &&
2376
            ((strlen($callbackuri) > 0) ||
2377
             (isset($clientparams['code'])))
2378
        ) {
2379
            // Extra check for new users: see if any HTML entities
2380
            // are in the user name. If so, send an email alert.
2381
            $dn = Util::getSessionVar('dn');
2382
            $dn = static::reformatDN(preg_replace('/\s+email=.+$/', '', $dn));
2383
            $htmldn = Util::htmlent($dn);
2384
            if (strcmp($dn, $htmldn) != 0) {
2385
                Util::sendErrorAlert(
2386
                    'New user DN contains HTML entities',
2387
                    "htmlentites(DN) = $htmldn\n"
2388
                );
2389
            }
2390
        }
2391
2392
        // For a new user, or if the user got new attributes, just log it.
2393
        // Then proceed to the Main Page.
2394
        if ($status == DBService::$STATUS['STATUS_NEW_USER']) {
2395
            $log->info('New User.');
2396
        } elseif ($status == DBService::$STATUS['STATUS_USER_UPDATED']) {
2397
            $log->info('User IdP attributes changed.');
2398
        }
2399
        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

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