Passed
Push — master ( 8e53e6...bcf872 )
by Terrence
14:00
created

Content::handleGotUser()   D

Complexity

Conditions 19
Paths 49

Size

Total Lines 133
Code Lines 94

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 380

Importance

Changes 6
Bugs 1 Features 0
Metric Value
cc 19
eloc 94
c 6
b 1
f 0
nc 49
nop 0
dl 0
loc 133
ccs 0
cts 65
cp 0
crap 380
rs 4.5166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

1430
        /** @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...
1431
        $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

1431
        /** @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...
1432
        $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

1432
        /** @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...
1433
        $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

1433
        /** @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...
1434
        $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

1434
        /** @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...
1435
        $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

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

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

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

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

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

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

2236
            /** @scrutinizer ignore-call */ 
2237
            printMainPage();
Loading history...
2237
            return;
2238
        }
2239
2240
        // Check for OIDC redirect_uri or OAuth 1.0a failureuri.
2241
        // If found, set 'Proceed' button redirect appropriately.
2242
        $redirect = '';
2243
        $redirectform = '';
2244
        // First, check for OIDC redirect_uri, with parameters in <form>
2245
        if (isset($clientparams['redirect_uri'])) {
2246
            $redirect = $clientparams['redirect_uri'];
2247
            $redirectform = '<input type="hidden" name="error" value="access_denied" />' .
2248
                '<input type="hidden" name="error_description" value="Missing attributes" />';
2249
            if (isset($clientparams['state'])) {
2250
                $redirectform .= '<input type="hidden" name="state" value="' .
2251
                    $clientparams['state'] . '" />';
2252
            }
2253
        }
2254
2255
        // Next, check for OAuth 1.0a
2256
        if ((strlen($redirect) == 0) && (strlen($failureuri) > 0)) {
2257
            $redirect = $failureuri . "?reason=missing_attributes";
2258
        }
2259
2260
        $isEduGAINAndGetCert = Util::isEduGAINAndGetCert($idp, $idpname);
2261
2262
        // Check for various error conditions and print out appropriate page
2263
        if (
2264
            (strlen($uid) == 0) ||    // Empty uid
2265
            (strlen($status) == 0) || // Empty status
2266
            ($status & 1) ||          // Odd-numbered status = error
2267
            ($isEduGAINAndGetCert)    // Not allowed
2268
        ) {
2269
            $log->error(
2270
                'Failed to getuser' .
2271
                ($isEduGAINAndGetCert ? ' due to eduGAIN IdP restriction.' : '.')
2272
            );
2273
2274
            // Is this a SAML IdP?
2275
            $idplist = Util::getIdpList();
2276
            $samlidp = ((!empty($idp)) && (!$idplist->isOAuth2($idp)));
2277
2278
            // Was there a misssing parameter?
2279
            $missingparam = ($status ==
2280
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']);
2281
2282
            if (($isEduGAINAndGetCert) || ($missingparam && $samlidp)) {
2283
                static::printSAMLAttributeReleaseErrorPage(
2284
                    $ePPN,
2285
                    $ePTID,
2286
                    $firstname,
2287
                    $lastname,
2288
                    $displayname,
2289
                    $emailaddr,
2290
                    $idp,
2291
                    $idpname,
2292
                    $affiliation,
2293
                    $ou,
2294
                    $memberof,
2295
                    $acr,
2296
                    $entitlement,
2297
                    $itrustuin,
2298
                    $clientparams,
2299
                    $redirect,
2300
                    $redirectform,
2301
                    $isEduGAINAndGetCert
2302
                );
2303
            } elseif ($missingparam && (!$samlidp)) { // OAuth2 IdP
2304
                static::printOAuth2AttributeReleaseErrorPage(
2305
                    $idpname,
2306
                    $redirect,
2307
                    $redirectform
2308
                );
2309
            } else { // General error
2310
                static::printGeneralErrorPage($redirect, $redirectform);
2311
            }
2312
        } else { // EVERYTHING IS OKAY SO FAR
2313
            // Extra security check: Once the user has successfully
2314
            // authenticated with an IdP, verify that the chosen IdP was
2315
            // actually whitelisted. If not, then set error message and show
2316
            // Select an Identity Provider page again.
2317
            Util::getSkin()->init();  // Check for forced skin
2318
            $idps = static::getCompositeIdPList();
2319
            $providerId = Util::getSessionVar('idp');
2320
            if ((strlen($providerId) > 0) && (!isset($idps[$providerId]))) {
2321
                Util::setSessionVar(
2322
                    'logonerror',
2323
                    'Invalid IdP selected. Please try again.'
2324
                );
2325
                Util::sendErrorAlert(
2326
                    'Authentication attempt using non-whitelisted IdP',
2327
                    'A user successfully authenticated with an IdP,
2328
                    however, the selected IdP was not in the list of
2329
                    whitelisted IdPs as determined by the current skin. This
2330
                    might indicate the user attempted to circumvent the
2331
                    security check in "handleGotUser()" for valid IdPs for
2332
                    the skin.'
2333
                );
2334
                Util::unsetCookieVar('providerId');
2335
                Util::unsetAllUserSessionVars();
2336
                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

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

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