Passed
Push — master ( 1a64e0...360e34 )
by Terrence
12:03
created

Util::getLOA()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 9
ccs 0
cts 0
cp 0
crap 6
rs 10
1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\CSRF;
6
use CILogon\Service\Loggit;
7
use CILogon\Service\IdpList;
8
use CILogon\Service\DBService;
9
use CILogon\Service\SessionMgr;
10
use CILogon\Service\Skin;
11
use CILogon\Service\TimeIt;
12
use CILogon\Service\PortalCookie;
13
use PEAR;
14
use DB;
15
use Config;
16
17
/**
18
 * Util
19
 *
20
 * This class contains a bunch of static (class) utility
21
 * methods, for example getting and setting server environment
22
 * variables and handling cookies. See the header for each function for
23
 * detailed description.
24
 */
25
class Util
26
{
27
    /**
28
     * @var array $ini_array Read the cilogon.ini file into an array
29
     */
30
    public static $ini_array = null;
31
32
    /**
33
     * @var TimeIt $timeit Initialize by calling static::startTiming() in
34
     * init().
35
     */
36
    public static $timeit;
37
38
    /**
39
     * @var IdPList $idplist A 'global' IdpList object since dplist.xml is
40
     *      large and expensive to create multiple times.
41
     */
42
    public static $idplist = null;
43
44
    /**
45
     * @var CSRF $csrf A 'global' CSRF token object to set the CSRF cookie
46
     * and print the hidden CSRF form element. Needs to be set only once
47
     * to keep the same CSRF value through the session.
48
     */
49
    public static $csrf = null;
50
51
    /**
52
     * @var Skin $skin A 'global' Skin object for skin configuration.
53
     */
54
    public static $skin = null;
55
56
    /**
57
     * @var array $oauth2idps An array of OAuth2 Identity Providers.
58
     */
59
    public static $oauth2idps = ['Google', 'GitHub', 'ORCID'];
60
61
62
    /**
63
     * getIdPList
64
     *
65
     * This function initializes the class $idplist object (if not yet
66
     * created) and returns it. This allows for a single 'global'
67
     * $idplist to be used by other classes (since creating an IdPList
68
     * object is expensive).
69
     *
70
     * @return IdPList|null The class instantiated IdPList object.
71
     **/
72
    public static function getIdpList()
73
    {
74
        if (is_null(static::$idplist)) {
75
            static::$idplist = new IdpList();
76
        }
77
        return static::$idplist;
78
    }
79
80
    /**
81
     * getCsrf
82
     *
83
     * This function initializes the class $csrf object (if not yet
84
     * created) and returns it. This allows for a single 'global'
85
     * $csrf to be used by other classes (since we want the CSRV value
86
     * to be consistent for the current page load).
87
     *
88
     * @return CSRF|null The class instantiated CSRF object.
89
     */
90
    public static function getCsrf()
91
    {
92
        if (is_null(static::$csrf)) {
93
            static::$csrf = new CSRF();
94
        }
95
        return static::$csrf;
96
    }
97
98
    /**
99
     * getSkin
100
     *
101
     * This function initializes the class $skin object (if not yet
102
     * created) and returns it. This allows for a single 'global'
103
     * $skin to be used by other classes (since loading the skin is
104
     * potentially expensive).
105
     *
106
     * @return Skin|null The class instantiated Skin object.
107
     */
108
    public static function getSkin()
109
    {
110
        if (is_null(static::$skin)) {
111
            static::$skin = new Skin();
112
        }
113
        return static::$skin;
114
    }
115
116
    /**
117
     * startTiming
118
     *
119
     * This function initializes the class variable $timeit which is
120
     * used for timing/benchmarking purposes.
121
     */
122
    public static function startTiming()
123
    {
124
        static::$timeit = new TimeIt(TimeIt::DEFAULTFILENAME, true);
125
    }
126
127
    /**
128
     * getServerVar
129
     *
130
     * This function queries a given $_SERVER variable (which is set
131
     * by the Apache server) and returns the value.
132
     *
133
     * @param string $serv The $_SERVER variable to query.
134
     * @return string The value of the $_SERVER variable or empty string
135
     *         if that variable is not set.
136
     */
137
    public static function getServerVar($serv)
138
    {
139
        $retval = '';
140
        if (isset($_SERVER[$serv])) {
141
            $retval = $_SERVER[$serv];
142
        }
143
        return $retval;
144
    }
145
146
    /**
147
     * getGetVar
148
     *
149
     * This function queries a given $_GET parameter (which is set in
150
     * the URL via a '?parameter=value' parameter) and returns the
151
     * value.
152
     *
153
     * @param string $get The $_GET variable to query.
154
     * @return string The value of the $_GET variable or empty string if
155
     *         that variable is not set.
156
     */
157
    public static function getGetVar($get)
158
    {
159
        $retval = '';
160
        if (isset($_GET[$get])) {
161
            $retval = $_GET[$get];
162
        }
163
        return $retval;
164
    }
165
166
    /**
167
     * getPostVar
168
     *
169
     * This function queries a given $_POST variable (which is set when
170
     * the user submits a form, for example) and returns the value.
171
     *
172
     * @param string $post The $_POST variable to query.
173
     * @return string The value of the $_POST variable or empty string if
174
     *         that variable is not set.
175
     */
176
    public static function getPostVar($post)
177
    {
178
        $retval = '';
179
        if (isset($_POST[$post])) {
180
            $retval = $_POST[$post];
181
        }
182
        return $retval;
183
    }
184
185
    /**
186
     * getGetOrPostVar
187
     *
188
     * This function looks for a $_GET or $_POST variable, with
189
     * preference given to $_GET if both are present.
190
     *
191
     * @param string $var The $_GET or $_POST variable to query.
192
     * @return string The value of the $_GET or $_POST variable
193
     *         if present. Empty string if variable is not set.
194
     */
195
    public static function getGetOrPostVar($var)
196
    {
197
        $retval = static::getGetVar($var);
198
        if (empty($retval)) {
199
            $retval = static::getPostVar($var);
200
        }
201
        return $retval;
202
    }
203
204
    /**
205
     * getCookieVar
206
     *
207
     * This function returns the value of a given cookie.
208
     *
209
     * @param string $cookie he $_COOKIE variable to query.
210
     * @return string The value of the $_COOKIE variable or empty string
211
     *         if that variable is not set.
212
     */
213
    public static function getCookieVar($cookie)
214
    {
215
        $retval = '';
216
        if (isset($_COOKIE[$cookie])) {
217
            $retval = $_COOKIE[$cookie];
218
        }
219
        return $retval;
220
    }
221
222
    /**
223
     * setCookieVar
224
     *
225
     * This function sets a cookie.
226
     *
227
     * @param string $cookie The name of the cookie to set.
228
     * @param string $value (Optional) The value to set for the cookie.
229
     *        Defaults to empty string.
230
     * @param int $exp The future expiration time (in seconds) of the
231
     *        cookie. Defaults to 1 year from now. If set to 0,
232
     *        the cookie expires at the end of the session.
233
     */
234
    public static function setCookieVar($cookie, $value = '', $exp = 31536000)
235
    {
236
        if ($exp > 0) {
237
            $exp += time();
238
        }
239
        setcookie($cookie, $value, $exp, '/', '.' . static::getDN(), true);
240
        $_COOKIE[$cookie] = $value;
241
    }
242
243
    /**
244
     * unsetCookieVar
245
     *
246
     * This function unsets a cookie. Strictly speaking, the cookie is
247
     * not removed, rather it is set to an empty value with an expired
248
     * time.
249
     *
250
     * @param string $cookie The name of the cookie to unset (delete).
251
     */
252
    public static function unsetCookieVar($cookie)
253
    {
254
        setcookie($cookie, '', 1, '/', '.' . static::getDN(), true);
255
        unset($_COOKIE[$cookie]);
256
    }
257
258
    /**
259
     * getPortalOrNormalCookieVar
260
     *
261
     * This is a convenience function which first checks if there is a
262
     * OAuth 1.0a ('delegate') or OIDC ('authorize') session active.
263
     * If so, it attempts to get the requested cookie from the
264
     * associated portalcookie. If there is not an OAuth/OIDC session
265
     * active, it looks for a 'normal' cookie. If you need a
266
     * portalcookie object to do multiple get/set method calls from
267
     * one function, it is probably better NOT to use this method since
268
     * creating the portalcookie object is potentially expensive.
269
     *
270
     * @param string $cookie The name of the cookie to get.
271
     * @return string The cookie value from either the portalcookie
272
     *         (in the case of an active OAuth session) or the
273
     *         'normal' cookie. Return empty string if no matching
274
     *         cookie in either place.
275
     */
276
    public static function getPortalOrNormalCookieVar($cookie)
277
    {
278
        $retval = '';
279
        $pc = new PortalCookie();
280
        $pn = $pc->getPortalName();
281
        if (strlen($pn) > 0) {
282
            $retval = $pc->get($cookie);
283
        } else {
284
            $retval = static::getCookieVar($cookie);
285
        }
286
        return $retval;
287
    }
288
289
    /**
290
     * getSessionVar
291
     *
292
     * This function returns the value of a given PHP Session variable.
293
     *
294
     * @param string $sess The $_SESSION variable to query.
295
     * @return string The value of the $_SESSION variable or empty string
296
     *         if that variable is not set.
297
     */
298
    public static function getSessionVar($sess)
299
    {
300
        $retval = '';
301
        if (isset($_SESSION[$sess])) {
302
            $retval = $_SESSION[$sess];
303
        }
304
        return $retval;
305
    }
306
307
    /**
308
     * setSessionVar
309
     *
310
     * This function can set or unset a given PHP session variable.
311
     * The first parameter is the PHP session variable to set/unset.
312
     * If the second parameter is the empty string, then the session
313
     * variable is unset.  Otherwise, the session variable is set to
314
     * the second parameter.  The function returns true if the session
315
     * variable was set to a non-empty value, false otherwise.
316
     * Normally, the return value can be ignored.
317
     *
318
     * @param string $key The name of the PHP session variable to set
319
     *        (or unset).
320
     * @param string $value (Optional) The value of the PHP session variable
321
     *        (to set), or empty string (to unset). Defaults to empty
322
     *        string (implies unset the session variable).
323
     * @return bool True if the PHP session variable was set to a
324
     *         non-empty string, false if variable was unset or if
325
     *         the specified session variable was not previously set.
326
     */
327
    public static function setSessionVar($key, $value = '')
328
    {
329
        $retval = false;  // Assume we want to unset the session variable
330
        if (strlen($key) > 0) {  // Make sure session var name was passed in
331
            if (strlen($value) > 0) {
332
                $_SESSION[$key] = $value;
333
                $retval = true;
334
            } else {
335
                static::unsetSessionVar($key);
336
            }
337
        }
338
        return $retval;
339
    }
340
341
    /**
342
     * unsetSessionVar
343
     *
344
     * This function clears the given PHP session variable by first
345
     * setting it to null and then unsetting it entirely.
346
     *
347
     * @param string $sess The $_SESSION variable to erase.
348
     */
349
    public static function unsetSessionVar($sess)
350
    {
351
        if (isset($_SESSION[$sess])) {
352
            $_SESSION[$sess] = null;
353
            unset($_SESSION[$sess]);
354
        }
355
    }
356
357
    /**
358
     * removeShibCookies
359
     *
360
     * This function removes all '_shib*' cookies currently in the
361
     * user's browser session. In effect, this logs the user out of
362
     * any IdP. Note that you must call this before you output any
363
     * HTML. Strictly speaking, the cookies are not removed, rather
364
     * they are set to empty values with expired times.
365
     */
366
    public static function removeShibCookies()
367
    {
368
        foreach ($_COOKIE as $key => $value) {
369
            if (strncmp($key, '_shib', strlen('_shib')) == 0) {
370
                static::unsetCookieVar($key);
371
            }
372
        }
373
    }
374
375
    /**
376
     * startPHPSession
377
     *
378
     * This function starts a secure PHP session and should be called
379
     * at the beginning of each script before any HTML is output.  It
380
     * does a trick of setting a 'lastaccess' time so that the
381
     * $_SESSION variable does not expire without warning.
382
     *
383
     * @param string $storetype (Optional) Storage location of the PHP
384
     *        session data, one of 'file' or 'mysql'. Defaults to null,
385
     *        which means use the value of STORAGE_PHPSESSIONS from the
386
     *        config.php file, or 'file' if no such parameter configured.
387
     */
388
    public static function startPHPSession($storetype = null)
0 ignored issues
show
Unused Code introduced by
The parameter $storetype 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

388
    public static function startPHPSession(/** @scrutinizer ignore-unused */ $storetype = null)

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...
389
    {
390
        // No parameter given? Use the value read in from cilogon.ini file.
391
        // If STORAGE_PHPSESSIONS == 'mysqli', create a sessionmgr().
392
        $storetype = STORAGE_PHPSESSIONS;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\STORAGE_PHPSESSIONS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
393
394
        if (preg_match('/^mysql/', $storetype)) {
395
            $sessionmgr = new SessionMgr();
0 ignored issues
show
Unused Code introduced by
The assignment to $sessionmgr is dead and can be removed.
Loading history...
396
        }
397
398
        ini_set('session.cookie_secure', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $newvalue of ini_set(). ( Ignorable by Annotation )

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

398
        ini_set('session.cookie_secure', /** @scrutinizer ignore-type */ true);
Loading history...
399
        ini_set('session.cookie_domain', '.' . static::getDN());
400
        session_start();
401
        if (
402
            (!isset($_SESSION['lastaccess']) ||
403
            (time() - $_SESSION['lastaccess']) > 60)
404
        ) {
405
            $_SESSION['lastaccess'] = time();
406
        }
407
    }
408
409
    /**
410
     * getScriptDir
411
     *
412
     * This function returns the directory (or full url) of the script
413
     * that is currently running.  The returned directory/url is
414
     * terminated by a '/' character (unless the second parameter is
415
     * set to true). This function is useful for those scripts named
416
     * index.php where we don't want to actually see 'index.php' in the
417
     * address bar (again, unless the second parameter is set to true).
418
     *
419
     * @param bool $prependhttp (Optional) Boolean to prepend 'http(s)://' to
420
     *        the script name. Defaults to false.
421
     * @param bool $stripfile (Optional) Boolean to strip off the trailing
422
     *        filename (e.g. index.php) from the path.
423
     *        Defaults to true (i.e., defaults to directory
424
     *        only without the trailing filename).
425
     * @return string The directory or url of the current script, with or
426
     *         without the trailing .php filename.
427
     */
428
    public static function getScriptDir($prependhttp = false, $stripfile = true)
429
    {
430
        $retval = static::getServerVar('SCRIPT_NAME');
431
        if ($stripfile) {
432
            $retval = dirname($retval);
433
        }
434
        if ($retval == '.') {
435
            $retval = '';
436
        }
437
        if (
438
            (strlen($retval) == 0) ||
439
            ($stripfile && ($retval[strlen($retval) - 1] != '/'))
440
        ) {
441
            $retval .= '/';  // Append a slash if necessary
442
        }
443
        if ($prependhttp) {  // Prepend http(s)://hostname
444
            $retval = 'http' .
445
                      ((strtolower(static::getServerVar('HTTPS')) == 'on') ? 's' : '') .
446
                      '://' . static::getServerVar('HTTP_HOST') . $retval;
447
        }
448
        return $retval;
449
    }
450
451
    /**
452
     * tempDir
453
     *
454
     * This function creates a temporary subdirectory within the
455
     * specified subdirectory. The new directory name is composed of
456
     * 16 hexadecimal letters, plus any prefix if you specify one. The
457
     * full path of the the newly created directory is returned.
458
     *
459
     * @param string $dir The full path to the containing directory.
460
     * @param string $prefix (Optional) A prefix for the new temporary
461
     *        directory. Defaults to empty string.
462
     * @param int $mode (Optional) Access permissions for the new
463
     *        temporary directory. Defaults to 0775.
464
     * @return string Full path to the newly created temporary directory.
465
     */
466
    public static function tempDir($dir, $prefix = '', $mode = 0775)
467
    {
468
        if (substr($dir, -1) != '/') {
469
            $dir .= '/';
470
        }
471
472
        $path = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $path is dead and can be removed.
Loading history...
473
        do {
474
            $path = $dir . $prefix . sprintf("%08X%08X", mt_rand(), mt_rand());
475
        } while (!mkdir($path, $mode, true));
476
477
        return $path;
478
    }
479
480
    /**
481
     * deleteDir
482
     *
483
     * This function deletes a directory and all of its contents.
484
     *
485
     * @param string $dir The (possibly non-empty) directory to delete.
486
     * @param bool $shred (Optional) Shred the file before deleting?
487
     *        Defaults to false.
488
     */
489
    public static function deleteDir($dir, $shred = false)
490
    {
491
        if (is_dir($dir)) {
492
            $objects = scandir($dir);
493
            foreach ($objects as $object) {
494
                if ($object != "." && $object != "..") {
495
                    if (filetype($dir . "/" . $object) == "dir") {
496
                        static::deleteDir($dir . "/" . $object);
497
                    } else {
498
                        if ($shred) {
499
                            @exec('/bin/env /usr/bin/shred -u -z ' . $dir . "/" . $object);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for exec(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

499
                            /** @scrutinizer ignore-unhandled */ @exec('/bin/env /usr/bin/shred -u -z ' . $dir . "/" . $object);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
500
                        } else {
501
                            @unlink($dir . "/" . $object);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

501
                            /** @scrutinizer ignore-unhandled */ @unlink($dir . "/" . $object);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
502
                        }
503
                    }
504
                }
505
            }
506
            reset($objects);
0 ignored issues
show
Bug introduced by
It seems like $objects can also be of type false; however, parameter $array of reset() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

506
            reset(/** @scrutinizer ignore-type */ $objects);
Loading history...
507
            @rmdir($dir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

507
            /** @scrutinizer ignore-unhandled */ @rmdir($dir);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
508
        }
509
    }
510
511
    /**
512
     * htmlent
513
     *
514
     * This method is necessary since htmlentities() does not seem to
515
     * obey the default arguments as documented in the PHP manual, and
516
     * instead encodes accented characters incorrectly. By specifying
517
     * the flags and encoding, the problem is solved.
518
     *
519
     * @param string $str : A string to process with htmlentities().
520
     * @return string The input string processed by htmlentities with
521
     *         specific options.
522
     */
523
    public static function htmlent($str)
524
    {
525
        return htmlentities($str, ENT_COMPAT | ENT_HTML401, 'UTF-8');
526
    }
527
528
    /**
529
     * sendErrorAlert
530
     *
531
     * Use this function to send an error message. The $summary should
532
     * be a short description of the error since it is placed in the
533
     * subject of the email. Put a more verbose description of the
534
     * error in the $detail parameter. Any session variables available
535
     * are appended to the body of the message.
536
     *
537
     * @param string $summary A brief summary of the error (in email subject)
538
     * @param string $detail A detailed description of the error (in the
539
     *        email body)
540
     * @param string $mailto (Optional) The destination email address.
541
     *        Defaults to '[email protected]'.
542
     */
543
    public static function sendErrorAlert(
544
        $summary,
545
        $detail,
546
        $mailto = '[email protected]'
547
    ) {
548
        $sessionvars = array(
549
            'idp'          => 'IdP ID',
550
            'idpname'      => 'IdP Name',
551
            'uid'          => 'Database UID',
552
            'dn'           => 'Cert DN',
553
            'firstname'    => 'First Name',
554
            'lastname'     => 'Last Name',
555
            'displayname'  => 'Display Name',
556
            'ePPN'         => 'ePPN',
557
            'ePTID'        => 'ePTID',
558
            'openID'       => 'OpenID ID',
559
            'oidcID'       => 'OIDC ID',
560
            'loa'          => 'LOA',
561
            'affiliation'  => 'Affiliation',
562
            'ou'           => 'OU',
563
            'memberof'     => 'MemberOf',
564
            'acr'          => 'AuthnContextClassRef',
565
            'entitlement'  => 'Entitlement',
566
            'itrustuin'    => 'iTrustUIN',
567
            'subjectID'    => 'Subject ID',
568
            'pairwiseID'   => 'Pairwise ID',
569
            'cilogon_skin' => 'Skin Name',
570
            'authntime'    => 'Authn Time'
571
        );
572
573
        $remoteaddr = static::getServerVar('REMOTE_ADDR');
574
        $remotehost = gethostbyaddr($remoteaddr);
575
        $mailfrom = 'From: [email protected]' . "\r\n" .
576
                    'X-Mailer: PHP/' . phpversion();
577
        $mailsubj = 'CILogon Service on ' . php_uname('n') .
578
                    ' - ' . $summary;
579
        $mailmsg  = '
580
CILogon Service - ' . $summary . '
581
-----------------------------------------------------------
582
' . $detail . '
583
584
Session Variables
585
-----------------
586
Timestamp     = ' . date(DATE_ATOM) . '
587
Server Host   = ' . static::getHN() . '
588
Remote Address= ' . $remoteaddr . '
589
' . (($remotehost !== false) ? "Remote Host   = $remotehost" : '') . '
590
';
591
592
        foreach ($sessionvars as $svar => $sname) {
593
            if (strlen($val = static::getSessionVar($svar)) > 0) {
594
                $mailmsg .= sprintf("%-14s= %s\n", $sname, $val);
595
            }
596
        }
597
598
        mail($mailto, $mailsubj, $mailmsg, $mailfrom);
599
    }
600
601
    /**
602
     * getFirstAndLastName
603
     *
604
     * This function attempts to get the first and last name of a user
605
     * extracted from the 'full name' (displayName) of the user.
606
     * Simply pass in all name info (full, first, and last) and the
607
     * function first tries to break up the full name into first/last.
608
     * If this is not sufficient, the function checks first and last
609
     * name. Finally, if either first or last is blank, the function
610
     * duplicates first <=> last so both names have the same value.
611
     * Note that even with all this, you still need to check if the
612
     * returned (first,last) names are blank.
613
     *
614
     * @param string $full The 'full name' of the user
615
     * @param string $first (Optional) The 'first name' of the user
616
     * @param string $last (Optional) The 'last name' of the user
617
     * @return array An array 'list(firstname,lastname)'
618
     */
619
    public static function getFirstAndLastName($full, $first = '', $last = '')
620
    {
621
        $firstname = '';
622
        $lastname = '';
623
624
        # Try to split the incoming $full name into first and last names
625
        if (strlen($full) > 0) {
626
            $names = preg_split('/\s+/', $full, 2);
627
            $firstname = @$names[0];
628
            $lastname =  @$names[1];
629
        }
630
631
        # If either first or last name blank, then use incoming $first and $last
632
        if (strlen($firstname) == 0) {
633
            $firstname = $first;
634
        }
635
        if (strlen($lastname) == 0) {
636
            $lastname = $last;
637
        }
638
639
        # Finally, if only a single name, copy first name <=> last name
640
        if (strlen($lastname) == 0) {
641
            $lastname = $firstname;
642
        }
643
        if (strlen($firstname) == 0) {
644
            $firstname = $lastname;
645
        }
646
647
        # Return both names as an array (i.e., use list($first,last)=...)
648
        return array($firstname,$lastname);
649
    }
650
651
    /**
652
     * getHN
653
     *
654
     * This function calculates and returns the 'hostname' for the
655
     * server. It first checks HTTP_HOST. If not set, it returns
656
     * DEFAULT_HOSTNAME. This is needed by command line scripts.
657
     *
658
     * @return string The 'Hostname' for the web server.
659
     */
660
    public static function getHN()
661
    {
662
        $thehostname = static::getServerVar('HTTP_HOST');
663
        if (strlen($thehostname) == 0) {
664
            $thehostname = 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...
665
        }
666
        return $thehostname;
667
    }
668
669
    /**
670
     * getDN
671
     *
672
     * This function calculates and returns the 'domainname' for the
673
     * server. It uses the hostname value calculated by getHN() and
674
     * uses the last two segments.
675
     *
676
     * @return string The 'Domainname' for the web server.
677
     */
678
    public static function getDN()
679
    {
680
        $thedomainname = static::getHN();
681
        if (preg_match('/[^\.]+\.[^\.]+$/', $thedomainname, $matches)) {
682
            $thedomainname = $matches[0];
683
        }
684
        return $thedomainname;
685
    }
686
687
    /**
688
     * getAuthzUrl
689
     *
690
     * This funtion takes in the name of an IdP (e.g., 'Google') and
691
     * returns the assoicated OAuth2 authorization URL.
692
     *
693
     * @param string $idp The name of an OAuth2 Identity Provider.
694
     * @return string The authorization URL for the given IdP.
695
     */
696
    public static function getAuthzUrl($idp)
697
    {
698
        $url = null;
699
        $idptourl = array(
700
            'Google' => 'https://accounts.google.com/o/oauth2/auth',
701
            'GitHub' => 'https://github.com/login/oauth/authorize',
702
            'ORCID'  => 'https://orcid.org/oauth/authorize',
703
        );
704
        if (array_key_exists($idp, $idptourl)) {
705
            $url = $idptourl[$idp];
706
        }
707
        return $url;
708
    }
709
710
    /**
711
     * getAuthzIdP
712
     *
713
     * This function takes in the OAuth2 authorization URL and returns
714
     * the associated pretty-print name of the IdP.
715
     *
716
     * @param string $url The authorization URL of an OAuth2 Identity Provider.
717
     * @return string The name of the IdP.
718
     */
719
    public static function getAuthzIdP($url)
720
    {
721
        $idp = null;
722
        $urltoidp = array(
723
            'https://accounts.google.com/o/oauth2/auth' => 'Google',
724
            'https://github.com/login/oauth/authorize'  => 'GitHub',
725
            'https://orcid.org/oauth/authorize'         => 'ORCID',
726
        );
727
        if (array_key_exists($url, $urltoidp)) {
728
            $idp = $urltoidp[$url];
729
        }
730
        return $idp;
731
    }
732
733
    /**
734
     * gotUserAttributes
735
     *
736
     * This function returns true if the PHP session contains all of the
737
     * necessary user/IdP attributes to fetch an X.509 certificate. This
738
     * means that at least one of (remoteuser, ePPN, ePTID, openidID,
739
     * oidcID) must be set, as well as idp (entityId), idpname, firstname,
740
     * lastname, and emailaddr. Also, the emailaddr must conform to valid
741
     * email formatting.
742
     *
743
     * @return bool True if all user/IdP attributes necessary to form the
744
     *              distinguished name (DN) for X.509 certificates are
745
     *              present in the PHP session. False otherwise.
746
     */
747
    public static function gotUserAttributes()
748
    {
749
        $retval = false;  // Assume we don't have all user attributes
750
        if (
751
            ((strlen(Util::getSessionVar('remoteuser')) > 0) ||
752
                (strlen(Util::getSessionVar('ePPN')) > 0) ||
753
                (strlen(Util::getSessionVar('ePTID')) > 0) ||
754
                (strlen(Util::getSessionVar('openidID')) > 0) ||
755
                (strlen(Util::getSessionVar('oidcID')) > 0)) &&
756
            (strlen(Util::getSessionVar('idp')) > 0) &&
757
            (strlen(Util::getSessionVar('idpname')) > 0)  &&
758
            (strlen(Util::getSessionVar('firstname')) > 0) &&
759
            (strlen(Util::getSessionVar('lastname')) > 0) &&
760
            (strlen(Util::getSessionVar('emailaddr')) > 0) &&
761
            (filter_var(Util::getSessionVar('emailaddr'), FILTER_VALIDATE_EMAIL))
762
        ) {
763
            $retval = true;
764
        }
765
        return $retval;
766
    }
767
768
    /**
769
     * saveUserToDataStore
770
     *
771
     * This function is called when a user logs on to save identity
772
     * information to the datastore. As it is used by both Shibboleth
773
     * and OpenID Identity Providers, some parameters passed in may
774
     * be blank (empty string). If the function verifies that the minimal
775
     * sets of parameters are valid, the dbservice servlet is called
776
     * to save the user info. Then various session variables are set
777
     * for use by the program later on. In case of error, an email
778
     * alert is sent showing the missing parameters.
779
     *
780
     * @param mixed $args Variable number of paramters ordered as follows:
781
     *     remoteuser -The REMOTE_USER from HTTP headers
782
     *     idp - The provider IdP Identifier / URL endpoint
783
     *     idpname - The pretty print provider IdP name
784
     *     firstname - The user's first name
785
     *     lastname - The user's last name
786
     *     displayname - The user's display name
787
     *     emailaddr-  The user's email address
788
     *     loa - The level of assurance (e.g., openid/basic/silver)
789
     *     ePPN - User's ePPN (for SAML IdPs)
790
     *     ePTID - User's ePTID (for SAML IdPs)
791
     *     openidID - User's OpenID 2.0 Identifier (Google deprecated)
792
     *     oidcID - User's OpenID Connect Identifier
793
     *     affiliation - User's affiliation
794
     *     ou - User's organizational unit (OU)
795
     *     memberof - User's isMemberOf group info
796
     *     acr - Authentication Context Class Ref
797
     *     entitlement - User's entitlement
798
     *     itrustuin - User's univerity ID number
799
     *     subjectID - User's university subject identifier
800
     *     pairwiseID - User's university pairwise identifier
801
     */
802
    public static function saveUserToDataStore(...$args)
803
    {
804
        $dbs = new DBService();
805
806
        // Save the passed-in variables to the session for later use
807
        // (e.g., by the error handler in handleGotUser). Then get these
808
        // session variables into local vars for ease of use.
809
        static::setUserAttributeSessionVars(...$args);
810
811
        $remoteuser  = static::getSessionVar('remoteuser');
812
        $idp         = static::getSessionVar('idp');
813
        $idpname     = static::getSessionVar('idpname');
814
        $firstname   = static::getSessionVar('firstname');
815
        $lastname    = static::getSessionVar('lastname');
816
        $displayname = static::getSessionVar('displayname');
817
        $emailaddr   = static::getSessionvar('emailaddr');
818
        $loa         = static::getSessionVar('loa');
819
        $ePPN        = static::getSessionVar('ePPN');
820
        $ePTID       = static::getSessionVar('ePTID');
821
        $openidID    = static::getSessionVar('openidID');
822
        $oidcID      = static::getSessionVar('oidcID');
823
        $affiliation = static::getSessionVar('affiliation');
824
        $ou          = static::getSessionVar('ou');
825
        $memberof    = static::getSessionVar('memberof');
826
        $acr         = static::getSessionVar('acr');
827
        $entitlement = static::getSessionVar('entitlement');
828
        $itrustuin   = static::getSessionVar('itrustuin');
829
        $subjectID   = static::getSessionVar('subjectID');
830
        $pairwiseID  = static::getSessionVar('pairwiseID');
831
832
        // Make sure parameters are not empty strings, and email is valid
833
        // Must have at least one of remoteuser/ePPN/ePTID/openidID/oidcID
834
        if (static::gotUserAttributes()) {
835
            // For the new Google OAuth 2.0 endpoint, we want to keep the
836
            // old Google OpenID endpoint URL in the database (so user does
837
            // not get a new certificate subject DN). Change the idp
838
            // and idpname to the old Google OpenID values.
839
            if (
840
                ($idpname == 'Google+') ||
841
                ($idp == static::getAuthzUrl('Google'))
842
            ) {
843
                $idpname = 'Google';
844
                $idp = 'https://www.google.com/accounts/o8/id';
845
            }
846
847
            // In the database, keep a consistent ProviderId format: only
848
            // allow 'http' (not 'https') and remove any 'www.' prefix.
849
            if ($loa == 'openid') {
850
                $idp = preg_replace('%^https://(www\.)?%', 'http://', $idp);
851
            }
852
853
            $result = $dbs->getUser(
854
                $remoteuser,
855
                $idp,
856
                $idpname,
857
                $firstname,
858
                $lastname,
859
                $displayname,
860
                $emailaddr,
861
                $ePPN,
862
                $ePTID,
863
                $openidID,
864
                $oidcID,
865
                $affiliation,
866
                $ou,
867
                $memberof,
868
                $acr,
869
                $entitlement,
870
                $itrustuin,
871
                $subjectID,
872
                $pairwiseID
873
            );
874
            static::setSessionVar('uid', $dbs->user_uid);
875
            static::setSessionVar('dn', $dbs->distinguished_name);
876
            static::setSessionVar('status', $dbs->status);
877
            if (!$result) {
878
                static::sendErrorAlert(
879
                    'dbService Error',
880
                    'Error calling dbservice action "getUser" in ' .
881
                    'saveUserToDatastore() method.'
882
                );
883
            }
884
        } else { // Missing one or more required attributes
885
            static::setSessionVar(
886
                'status',
887
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']
888
            );
889
        }
890
891
        // If 'status' is not STATUS_OK*, then send an error email
892
        $status = static::getSessionVar('status');
893
        if ($status & 1) { // Bad status codes are odd
894
            // For missing parameter errors, log an error message
895
            if (
896
                $status ==
897
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']
898
            ) {
899
                $log = new Loggit();
900
                $log->error('STATUS_MISSING_PARAMETER_ERROR', true);
901
            }
902
903
            // For other dbservice errors OR for any error involving
904
            // LIGO (e.g., missing parameter error), send email alert.
905
            if (
906
                ($status !=
907
                    DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) ||
908
                (preg_match('/ligo\.org/', $idp))
909
            ) {
910
                $mailto = '[email protected]';
911
912
                // CIL-205 - Notify LIGO about IdP login errors.
913
                // Set DISABLE_LIGO_ALERTS to true in the top-level
914
                // config.php file to stop LIGO failures
915
                // from being sent to '[email protected]', but still
916
                // sent to '[email protected]'.
917
                if (preg_match('/ligo\.org/', $idp)) {
918
                    if (DISABLE_LIGO_ALERTS) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DISABLE_LIGO_ALERTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
919
                        $mailto = '';
920
                    }
921
                    $mailto .= ((strlen($mailto) > 0) ? ',' : '') .
922
                        '[email protected]';
923
                }
924
925
                static::sendErrorAlert(
926
                    'Failure in ' .
927
                        (($loa == 'openid') ? '' : '/secure') . '/getuser/',
928
                    'Remote_User   = ' . ((strlen($remoteuser) > 0) ?
929
                        $remoteuser : '<MISSING>') . "\n" .
930
                    'IdP ID        = ' . ((strlen($idp) > 0) ?
931
                        $idp : '<MISSING>') . "\n" .
932
                    'IdP Name      = ' . ((strlen($idpname) > 0) ?
933
                        $idpname : '<MISSING>') . "\n" .
934
                    'First Name    = ' . ((strlen($firstname) > 0) ?
935
                        $firstname : '<MISSING>') . "\n" .
936
                    'Last Name     = ' . ((strlen($lastname) > 0) ?
937
                        $lastname : '<MISSING>') . "\n" .
938
                    'Display Name  = ' . ((strlen($displayname) > 0) ?
939
                        $displayname : '<MISSING>') . "\n" .
940
                    'Email Address = ' . ((strlen($emailaddr) > 0) ?
941
                        $emailaddr : '<MISSING>') . "\n" .
942
                    'ePPN          = ' . ((strlen($ePPN) > 0) ?
943
                        $ePPN : '<MISSING>') . "\n" .
944
                    'ePTID         = ' . ((strlen($ePTID) > 0) ?
945
                        $ePTID : '<MISSING>') . "\n" .
946
                    'OpenID ID     = ' . ((strlen($openidID) > 0) ?
947
                        $openidID : '<MISSING>') . "\n" .
948
                    'OIDC ID       = ' . ((strlen($oidcID) > 0) ?
949
                        $oidcID : '<MISSING>') . "\n" .
950
                    'Affiliation   = ' . ((strlen($affiliation) > 0) ?
951
                        $affiliation : '<MISSING>') . "\n" .
952
                    'OU            = ' . ((strlen($ou) > 0) ?
953
                        $ou : '<MISSING>') . "\n" .
954
                    'MemberOf      = ' . ((strlen($memberof) > 0) ?
955
                        $memberof : '<MISSING>') . "\n" .
956
                    'ACR           = ' . ((strlen($acr) > 0) ?
957
                        $acr : '<MISSING>') . "\n" .
958
                    'Entitlement   = ' . ((strlen($entitlement) > 0) ?
959
                        $entitlement : '<MISSING>') . "\n" .
960
                    'iTrustUIN     = ' . ((strlen($itrustuin) > 0) ?
961
                        $itrustuin : '<MISSING>') . "\n" .
962
                    'Subject ID    = ' . ((strlen($subjectID) > 0) ?
963
                        $subjectID : '<MISSING>') . "\n" .
964
                    'Pairwise ID   = ' . ((strlen($pairwiseID) > 0) ?
965
                        $pairwiseID : '<MISSING>') . "\n" .
966
                    'Database UID  = ' . ((strlen(
967
                        $i = static::getSessionVar('uid')
968
                    ) > 0) ?  $i : '<MISSING>') . "\n" .
969
                    'Status Code   = ' . ((strlen(
0 ignored issues
show
Bug introduced by
Are you sure strlen($i = array_search... > 0 ? $i : '<MISSING>' of type false|integer|string can be used in concatenation? ( Ignorable by Annotation )

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

969
                    'Status Code   = ' . (/** @scrutinizer ignore-type */ (strlen(
Loading history...
970
                        $i = array_search(
0 ignored issues
show
Bug introduced by
It seems like $i = array_search($statu...vice\DBService::STATUS) can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

970
                        /** @scrutinizer ignore-type */ $i = array_search(
Loading history...
971
                            $status,
972
                            DBService::$STATUS
973
                        )
974
                    ) > 0) ?  $i : '<MISSING>'),
975
                    $mailto
976
                );
977
            }
978
            static::unsetSessionVar('authntime');
979
        }
980
    }
981
982
    /**
983
     * setUserAttributeSessionVars
984
     *
985
     * This method is called by saveUserToDatastore to put the passsed-in
986
     * variables into the PHP session for later use.
987
     *
988
     * @param mixed $args Variable number of user attribute paramters
989
     *        ordered as shown in the $attrs array below.
990
     */
991
    public static function setUserAttributeSessionVars(...$args)
992
    {
993
        $attrs = array('remoteuser', 'idp', 'idpname', 'firstname',
994
                       'lastname', 'displayname', 'emailaddr',
995
                       'loa', 'ePPN', 'ePTID', 'openidID', 'oidcID',
996
                       'affiliation', 'ou', 'memberof', 'acr',
997
                       'entitlement', 'itrustuin', 'subjectID',
998
                       'pairwiseID');
999
        $numargs = count($args);
1000
        for ($i = 0; $i < $numargs; $i++) {
1001
            static::setSessionVar($attrs[$i], $args[$i]);
1002
        }
1003
1004
        static::setSessionVar('status', '0');
1005
        static::setSessionVar('submit', static::getSessionVar('responsesubmit'));
1006
        static::setSessionVar('authntime', time());
1007
        static::unsetSessionVar('responsesubmit');
1008
        static::getCsrf()->setCookieAndSession();
1009
    }
1010
1011
    /**
1012
     * unsetClientSessionVars
1013
     *
1014
     * This function removes all of the PHP session variables related to
1015
     * the client session.
1016
     */
1017
    public static function unsetClientSessionVars()
1018
    {
1019
        static::unsetSessionVar('submit');
1020
1021
        // Specific to 'Download Certificate' page
1022
        static::unsetSessionVar('p12');
1023
        static::unsetSessionVar('p12lifetime');
1024
        static::unsetSessionVar('p12multiplier');
1025
1026
        // Specific to OAuth 1.0a flow
1027
        static::unsetSessionVar('portalstatus');
1028
        static::unsetSessionVar('callbackuri');
1029
        static::unsetSessionVar('successuri');
1030
        static::unsetSessionVar('failureuri');
1031
        static::unsetSessionVar('portalname');
1032
        static::unsetSessionVar('tempcred');
1033
1034
        // Specific to OIDC flow
1035
        static::unsetSessionVar('clientparams');
1036
    }
1037
1038
    /**
1039
     * unsetUserSessionVars
1040
     *
1041
     * This function removes all of the PHP session variables related to
1042
     * the user's session.  This will force the user to log on (again)
1043
     * with their IdP and call the 'getuser' script to repopulate the PHP
1044
     * session.
1045
     */
1046
    public static function unsetUserSessionVars()
1047
    {
1048
        // Needed for verifyCurrentUserSession
1049
        static::unsetSessionVar('idp');
1050
        static::unsetSessionVar('idpname');
1051
        static::unsetSessionVar('status');
1052
        static::unsetSessionVar('uid');
1053
        static::unsetSessionVar('dn');
1054
        static::unsetSessionVar('authntime');
1055
1056
        // Variables set by getuser
1057
        static::unsetSessionVar('firstname');
1058
        static::unsetSessionVar('lastname');
1059
        static::unsetSessionVar('displayname');
1060
        static::unsetSessionVar('emailaddr');
1061
        static::unsetSessionVar('loa');
1062
        static::unsetSessionVar('ePPN');
1063
        static::unsetSessionVar('ePTID');
1064
        static::unsetSessionVar('openidID');
1065
        static::unsetSessionVar('oidcID');
1066
        static::unsetSessionVar('affiliation');
1067
        static::unsetSessionVar('ou');
1068
        static::unsetSessionVar('memberof');
1069
        static::unsetSessionVar('acr');
1070
        static::unsetSessionVar('entitlement');
1071
        static::unsetSessionVar('itrustuin');
1072
        static::unsetSessionVar('subjectID');
1073
        static::unsetSessionVar('pairwiseID');
1074
1075
        // Current skin
1076
        static::unsetSessionVar('cilogon_skin');
1077
    }
1078
1079
    /**
1080
     * unsetAllUserSessionVars
1081
     *
1082
     * This is a convenience method to clear all session variables related
1083
     * to the client and the user.
1084
     */
1085
    public static function unsetAllUserSessionVars()
1086
    {
1087
        static::unsetClientSessionVars();
1088
        static::unsetUserSessionVars();
1089
    }
1090
1091
    /**
1092
     * verifySessionAndCall
1093
     *
1094
     * This function is a convenience method called by several cases in the
1095
     * main 'switch' call at the top of the index.php file. I noticed
1096
     * a pattern where verifyCurrentUserSession() was called to verify the
1097
     * current user session. Upon success, one or two functions were called
1098
     * to continue program, flow. Upon failure, cookies and session
1099
     * variables were cleared, and the main Logon page was printed. This
1100
     * function encapsulates that pattern. If the user's session is valid,
1101
     * the passed-in $func is called, possibly with parameters passed in as
1102
     * an array. The function returns true if the session is verified, so
1103
     * that other functions may be called upon return.
1104
     *
1105
     * @param callable $func The function to call if the current session is
1106
     *        successfully verified.
1107
     * @param array $params (Optional) An array of parameters to pass to the
1108
     *        function. Defaults to empty array, meaning zero parameters.
1109
     */
1110
    public static function verifySessionAndCall($func, $params = array())
1111
    {
1112
        $retval = false;
1113
        if (Content::verifyCurrentUserSession()) { // Verify PHP session is valid
1114
            $retval = true;
1115
            call_user_func_array($func, $params);
1116
        } else {
1117
            printLogonPage(true); // Clear cookies and session vars too
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

1117
            /** @scrutinizer ignore-call */ 
1118
            printLogonPage(true); // Clear cookies and session vars too
Loading history...
1118
        }
1119
        return $retval;
1120
    }
1121
1122
    /**
1123
     * isEduGAINAndGetCert
1124
     *
1125
     * This function checks to see if the current session IdP is an
1126
     * eduGAIN IdP (i.e., not Registered By InCommon) and the IdP does not
1127
     * have both the REFEDS R&S and SIRTFI extensions in metadata. If so,
1128
     * check to see if the transaction could be used to fetch a
1129
     * certificate. (The only time the transaction is not used to fetch
1130
     * a cert is during OIDC without the 'getcert' scope.) If all that is
1131
     * true, then return true. Otherwise return false.
1132
     *
1133
     * @param string $idp (optional) The IdP entityID. If empty, read value
1134
     *        from PHP session.
1135
     * @param string $idpname (optional) The IdP display name. If empty,
1136
     *        read value from PHP session.
1137
     * @return bool True if the current IdP is an eduGAIN IdP without
1138
     *         both REFEDS R&S and SIRTFI, AND the session could be
1139
     *         used to get a certificate.
1140
     */
1141
    public static function isEduGAINAndGetCert($idp = '', $idpname = '')
1142
    {
1143
        $retval = false; // Assume not eduGAIN IdP and getcert
1144
1145
        // If $idp or $idpname not passed in, get from current session.
1146
        if (strlen($idp) == 0) {
1147
            $idp = static::getSessionVar('idp');
1148
        }
1149
        if (strlen($idpname) == 0) {
1150
            $idpname = static::getSessionVar('idpname');
1151
        }
1152
1153
        // Check if this was an OIDC transaction, and if the
1154
        // 'getcert' scope was requested.
1155
        $oidcscopegetcert = false;
1156
        $oidctrans = false;
1157
        $clientparams = json_decode(static::getSessionVar('clientparams'), true);
1158
        if (isset($clientparams['scope'])) {
1159
            $oidctrans = true;
1160
            if (
1161
                preg_match(
1162
                    '/edu\.uiuc\.ncsa\.myproxy\.getcert/',
1163
                    $clientparams['scope']
1164
                )
1165
            ) {
1166
                $oidcscopegetcert = true;
1167
            }
1168
        }
1169
1170
        // First, make sure $idp was set and is not an OAuth2 IdP.
1171
        $idplist = static::getIdpList();
1172
        if (
1173
            ((strlen($idp) > 0) &&
1174
            (strlen($idpname) > 0) &&
1175
            (!in_array($idpname, static::$oauth2idps))) &&
1176
                (
1177
                // Next, check for eduGAIN without REFEDS R&S and SIRTFI
1178
                ((!$idplist->isRegisteredByInCommon($idp)) &&
1179
                       ((!$idplist->isREFEDSRandS($idp)) ||
1180
                        (!$idplist->isSIRTFI($idp))
1181
                       )
1182
                ) &&
1183
                // Next, check if user could get X509 cert,
1184
                // i.e., OIDC getcert scope, or a non-OIDC
1185
                // transaction such as PKCS12, JWS, or OAuth 1.0a
1186
                ($oidcscopegetcert || !$oidctrans)
1187
                )
1188
        ) {
1189
            $retval = true;
1190
        }
1191
        return $retval;
1192
    }
1193
1194
    /**
1195
     * setPortalOrCookie
1196
     *
1197
     * This is a convenience function for a set of operations that is done
1198
     * a few times in Content.php. It first checks if the name of the portal
1199
     * in the PortalCookie is empty. If not, then it sets the PortalCookie
1200
     * key/value pair. Otherwise, it sets the 'normal' cookie key/value
1201
     * pair.
1202
     *
1203
     * @param PortalCookie $pc The PortalCookie to read/write. If the portal
1204
     *        name is empty, then use the 'normal' cookie instead.
1205
     * @param string $key The key of the PortalCookie or 'normal' cookie to
1206
     *        set.
1207
     * @param string $value The value to set for the $key.
1208
     * @param bool $save (optional) If set to true, attempt to write the
1209
     *        PortalCookie. Defaults to false.
1210
     */
1211
    public static function setPortalOrCookie($pc, $key, $value, $save = false)
1212
    {
1213
        $pn = $pc->getPortalName();
1214
        // If the portal name is valid, then set the PortalCookie key/value
1215
        if (strlen($pn) > 0) {
1216
            $pc->set($key, $value);
1217
            if ($save) {
1218
                $pc->write();
1219
            }
1220
        } else { // If portal name is not valid, then use the 'normal' cookie
1221
            if (strlen($value) > 0) {
1222
                Util::setCookieVar($key, $value);
1223
            } else { // If $value is empty, then UNset the 'normal' cookie
1224
                Util::unsetCookieVar($key);
1225
            }
1226
        }
1227
    }
1228
1229
    /**
1230
     * getOIDCClientParams
1231
     *
1232
     * This function addresses CIL-618 and reads OIDC client information
1233
     * directly from the database. It is a replacement for
1234
     * $dbs->getClient($clientparams['client_id']) which calls
1235
     * '/dbService?action=getClient&client_id=...'. This gives the PHP
1236
     * '/authorize' endpoint access to additional OIDC client parameters
1237
     * without having to rewrite the '/dbService?action=getClient' endpoint.
1238
     *
1239
     * @param array $clientparams An array of client parameters which gets
1240
     *              stored in the PHP session. The keys of the array are
1241
     *              the column names of the 'client' table in the 'ciloa2'
1242
     *              database, prefixed by 'client_'.
1243
     */
1244
    public static function getOIDCClientParams(&$clientparams)
1245
    {
1246
        $retval = false;
1247
        if (strlen(@$clientparams['client_id']) > 0) {
1248
            $dsn = array(
1249
                'phptype'  => 'mysqli',
1250
                'username' => MYSQLI_USERNAME,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYSQLI_USERNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1251
                'password' => MYSQLI_PASSWORD,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYSQLI_PASSWORD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1252
                'database' => 'ciloa2',
1253
                'hostspec' => 'localhost'
1254
            );
1255
1256
            $opts = array(
1257
                'persistent'  => true,
1258
                'portability' => DB_PORTABILITY_ALL
1259
            );
1260
1261
            $db = DB::connect($dsn, $opts);
1262
            if (!PEAR::isError($db)) {
1263
                $data = $db->getRow(
1264
                    'SELECT * from clients WHERE client_id = ?',
1265
                    array($clientparams['client_id']),
1266
                    DB_FETCHMODE_ASSOC
1267
                );
1268
                if (!DB::isError($data)) {
1269
                    if (!empty($data)) {
1270
                        foreach ($data as $key => $value) {
1271
                            $clientparams['client_' . $key] = $value;
1272
                        }
1273
                        $clientparams['clientstatus'] = DBService::$STATUS['STATUS_OK'];
1274
                        $retval = true;
1275
                    }
1276
                }
1277
                $db->disconnect();
1278
            }
1279
        }
1280
        return $retval;
1281
    }
1282
1283
    /**
1284
     * getMinMaxLifetimes
1285
     *
1286
     * This function checks the skin's configuration to see if either or
1287
     * both of minlifetime and maxlifetime in the specified config.xml
1288
     * block have been set. If not, default to minlifetime of 1 (hour) and
1289
     * the specified defaultmaxlifetime.
1290
     *
1291
     * @param string $section The XML section block from which to read the
1292
     *        minlifetime and maxlifetime values. Can be one of the
1293
     *        following: 'pkcs12' or 'delegate'.
1294
     * @param int $defaultmaxlifetime Default maxlifetime (in hours) for the
1295
     *        credential.
1296
     * @return array An array consisting of two entries: the minimum and
1297
     *         maximum lifetimes (in hours) for a credential.
1298
     */
1299
    public static function getMinMaxLifetimes($section, $defaultmaxlifetime)
1300
    {
1301
        $minlifetime = 1;    // Default minimum lifetime is 1 hour
1302
        $maxlifetime = $defaultmaxlifetime;
1303
        $skin = Util::getSkin();
1304
        $skinminlifetime = $skin->getConfigOption($section, 'minlifetime');
1305
        // Read the skin's minlifetime value from the specified section
1306
        if ((!is_null($skinminlifetime)) && ((int)$skinminlifetime > 0)) {
1307
            $minlifetime = max($minlifetime, (int)$skinminlifetime);
1308
            // Make sure $minlifetime is less than $maxlifetime;
1309
            $minlifetime = min($minlifetime, $maxlifetime);
1310
        }
1311
        // Read the skin's maxlifetime value from the specified section
1312
        $skinmaxlifetime = $skin->getConfigOption($section, 'maxlifetime');
1313
        if ((!is_null($skinmaxlifetime)) && ((int)$skinmaxlifetime) > 0) {
1314
            $maxlifetime = min($maxlifetime, (int)$skinmaxlifetime);
1315
            // Make sure $maxlifetime is greater than $minlifetime
1316
            $maxlifetime = max($minlifetime, $maxlifetime);
1317
        }
1318
1319
        return array($minlifetime, $maxlifetime);
1320
    }
1321
1322
    /**
1323
     * isLOASilver
1324
     *
1325
     * This function returns true if the 'loa' (level of assurance)
1326
     * should be http://incommonfederation.org/assurance/silver .
1327
     * As specified in CACC-238, this is when both of the following are true:
1328
     * (1) loa contains  https://refeds.org/assurance/profile/cappuccino
1329
     * (2) acr is either https://refeds.org/profile/sfa or
1330
     *                   https://refeds.org/profile/mfa
1331
     *
1332
     * @return bool True if level of assurance is 'silver'.
1333
     */
1334
    public static function isLOASilver()
1335
    {
1336
        $retval = false;
1337
        if (
1338
            (preg_match('%https://refeds.org/assurance/profile/cappuccino%', static::getSessionVar('loa'))) &&
1339
            (preg_match('%https://refeds.org/profile/[ms]fa%', static::getSessionVar('acr')))
1340
        ) {
1341
            $retval = true;
1342
        }
1343
        return $retval;
1344
    }
1345
1346
    /**
1347
     * getLOA
1348
     *
1349
     * This function is a bit of a hack. Once upon a time, the level of
1350
     * assurance (loa) was one of empty string (which implied 'basic
1351
     * CA'), 'openid' (which implied 'openid CA'), or
1352
     * 'http://incommonfederation.org/assurance/silver' (which implied
1353
     * 'silver CA'). Then things got more complex when the silver
1354
     * assurance was replaced by cappuccino (see CACC-238). But parts of the
1355
     * PHP code still depeneded on the InCommon silver string.
1356
     *
1357
     * This function transforms the assurance attribute asserted by an IdP
1358
     * (which is stored in the 'loa' session variable) into one of
1359
     * empty string (for 'basic CA'), 'openid', or
1360
     * 'http://incommonfederation.org/assurance/silver' for use by those
1361
     * PHP functions which expect the 'loa' in this format.
1362
     *
1363
     * @return string One of empty string, 'openid', or
1364
     *         'http://incommonfederation.org/assurance/silver'
1365
     */
1366
    public static function getLOA()
1367
    {
1368
        $retval = '';
1369
        if (static::isLOASilver()) {
1370
            $retval = 'http://incommonfederation.org/assurance/silver';
1371
        } else {
1372
            $retval = static::getSessionVar('loa');
1373
        }
1374
        return $retval;
1375
    }
1376
1377
    /**
1378
     * getLOAPort
1379
     *
1380
     * This function returns the port to be used for MyProxy based on the
1381
     * level of assurance.
1382
     *     Basic  CA = 7512
1383
     *     Silver CA = 7514
1384
     *     OpenID CA = 7516
1385
     *
1386
     * @return int The MyProxy port number to be used based on the 'level
1387
     *         of assurance' (basic, silver, openid).
1388
     */
1389
    public static function getLOAPort()
1390
    {
1391
        $port = 7512; // Basic
1392
        if (Util::isLOASilver()) {
1393
            $port = 7514;
1394
        } elseif (Util::getSessionVar('loa') == 'openid') {
1395
            $port = 7516;
1396
        }
1397
        return $port;
1398
    }
1399
}
1400