Passed
Push — master ( c5dd1c...562ec1 )
by Terrence
12:19
created

Util::sendErrorAlert()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 57
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 12
Bugs 0 Features 0
Metric Value
cc 4
eloc 41
c 12
b 0
f 0
nc 6
nop 3
dl 0
loc 57
ccs 0
cts 29
cp 0
crap 20
rs 9.264

How to fix   Long Method   

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

387
    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...
388
    {
389
        // No parameter given? Use the value read in from cilogon.ini file.
390
        // If STORAGE_PHPSESSIONS == 'mysqli', create a sessionmgr().
391
        $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...
392
393
        if (preg_match('/^mysql/', $storetype)) {
394
            $sessionmgr = new SessionMgr();
0 ignored issues
show
Unused Code introduced by
The assignment to $sessionmgr is dead and can be removed.
Loading history...
395
        } elseif ($storetype == 'file') {
396
            // If storing PHP sessions to file, check if an optional directory
397
            // for storage has been set. If so, create it if necessary.
398
            if ((defined('STORAGE_PHPSESSIONS_DIR')) && (!empty(STORAGE_PHPSESSIONS_DIR))) {
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\STORAGE_PHPSESSIONS_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
399
                if (!is_dir(STORAGE_PHPSESSIONS_DIR)) {
400
                    mkdir(STORAGE_PHPSESSIONS_DIR, 0770, true);
401
                }
402
403
                if (is_dir(STORAGE_PHPSESSIONS_DIR)) {
404
                    ini_set('session.save_path', STORAGE_PHPSESSIONS_DIR);
405
                }
406
            }
407
        }
408
409
        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

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

510
                            /** @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...
511
                        } else {
512
                            @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

512
                            /** @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...
513
                        }
514
                    }
515
                }
516
            }
517
            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

517
            reset(/** @scrutinizer ignore-type */ $objects);
Loading history...
518
            @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

518
            /** @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...
519
        }
520
    }
521
522
    /**
523
     * htmlent
524
     *
525
     * This method is necessary since htmlentities() does not seem to
526
     * obey the default arguments as documented in the PHP manual, and
527
     * instead encodes accented characters incorrectly. By specifying
528
     * the flags and encoding, the problem is solved.
529
     *
530
     * @param string $str : A string to process with htmlentities().
531
     * @return string The input string processed by htmlentities with
532
     *         specific options.
533
     */
534
    public static function htmlent($str)
535
    {
536
        return htmlentities($str, ENT_COMPAT | ENT_HTML401, 'UTF-8');
537
    }
538
539
    /**
540
     * sendErrorAlert
541
     *
542
     * Use this function to send an error message. The $summary should
543
     * be a short description of the error since it is placed in the
544
     * subject of the email. Put a more verbose description of the
545
     * error in the $detail parameter. Any session variables available
546
     * are appended to the body of the message.
547
     *
548
     * @param string $summary A brief summary of the error (in email subject)
549
     * @param string $detail A detailed description of the error (in the
550
     *        email body)
551
     * @param string $mailto (Optional) The destination email address.
552
     *        Defaults to EMAIL_ALERTS (defined in the top-level
553
     *        config.php file as 'alerts@' . DEFAULT_HOSTNAME).
554
     */
555
    public static function sendErrorAlert(
556
        $summary,
557
        $detail,
558
        $mailto = EMAIL_ALERTS
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\EMAIL_ALERTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
559
    ) {
560
        $sessionvars = array(
561
            'idp'                => 'IdP ID',
562
            'idp_display_name'   => 'IdP Name',
563
            'user_uid'           => 'User UID',
564
            'distinguished_name' => 'Cert DN',
565
            'first_name'         => 'First Name',
566
            'last_name'          => 'Last Name',
567
            'display_name'       => 'Display Name',
568
            'eppn'               => 'ePPN',
569
            'eptid'              => 'ePTID',
570
            'open_id'            => 'OpenID ID',
571
            'oidc'               => 'OIDC ID',
572
            'subject_id'         => 'Subject ID',
573
            'pairwise_id'        => 'Pairwise ID',
574
            'loa'                => 'LOA',
575
            'affiliation'        => 'Affiliation',
576
            'ou'                 => 'OU',
577
            'member_of'          => 'MemberOf',
578
            'acr'                => 'AuthnContextClassRef',
579
            'amr'                => 'AuthnMethodRef',
580
            'entitlement'        => 'Entitlement',
581
            'itrustuin'          => 'iTrustUIN',
582
            'cilogon_skin'       => 'Skin Name',
583
            'authntime'          => 'Authn Time'
584
        );
585
586
        $remoteaddr = static::getServerVar('REMOTE_ADDR');
587
        $remotehost = gethostbyaddr($remoteaddr);
588
        $mailfrom = 'From: ' . EMAIL_ALERTS . "\r\n" .
589
                    'X-Mailer: PHP/' . phpversion();
590
        $mailsubj = 'CILogon Service on ' . php_uname('n') .
591
                    ' - ' . $summary;
592
        $mailmsg  = '
593
CILogon Service - ' . $summary . '
594
-----------------------------------------------------------
595
' . $detail . '
596
597
Session Variables
598
-----------------
599
Timestamp     = ' . date(DATE_ATOM) . '
600
Server Host   = ' . static::getHN() . '
601
Remote Address= ' . $remoteaddr . '
602
' . (($remotehost !== false) ? "Remote Host   = $remotehost" : '') . '
603
';
604
605
        foreach ($sessionvars as $svar => $sname) {
606
            if (strlen($val = static::getSessionVar($svar)) > 0) {
607
                $mailmsg .= sprintf("%-14s= %s\n", $sname, $val);
608
            }
609
        }
610
611
        mail($mailto, $mailsubj, $mailmsg, $mailfrom);
612
    }
613
614
    /**
615
     * getHN
616
     *
617
     * This function calculates and returns the 'hostname' for the
618
     * server. It first checks HTTP_HOST. If not set, it returns
619
     * DEFAULT_HOSTNAME. This is needed by command line scripts.
620
     *
621
     * @return string The 'Hostname' for the web server.
622
     */
623
    public static function getHN()
624
    {
625
        $thehostname = static::getServerVar('HTTP_HOST');
626
        if (strlen($thehostname) == 0) {
627
            $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...
628
        }
629
        return $thehostname;
630
    }
631
632
    /**
633
     * getDN
634
     *
635
     * This function calculates and returns the 'domainname' for the
636
     * server. It uses the hostname value calculated by getHN() and
637
     * uses the last two segments.
638
     *
639
     * @return string The 'Domainname' for the web server.
640
     */
641
    public static function getDN()
642
    {
643
        $thedomainname = static::getHN();
644
        if (preg_match('/[^\.]+\.[^\.]+$/', $thedomainname, $matches)) {
645
            $thedomainname = $matches[0];
646
        }
647
        return $thedomainname;
648
    }
649
650
    /**
651
     * getAuthzUrl
652
     *
653
     * This funtion takes in the name of an IdP (e.g., 'Google') and
654
     * returns the assoicated OAuth2 authorization URL.
655
     *
656
     * @param string $idp The name of an OAuth2 Identity Provider.
657
     * @return string The authorization URL for the given IdP.
658
     */
659
    public static function getAuthzUrl($idp)
660
    {
661
        $url = null;
662
        $idptourl = array(
663
            'Google' => 'https://accounts.google.com/o/oauth2/auth',
664
            'GitHub' => 'https://github.com/login/oauth/authorize',
665
            'ORCID'  => 'https://orcid.org/oauth/authorize',
666
        );
667
        if (array_key_exists($idp, $idptourl)) {
668
            $url = $idptourl[$idp];
669
        }
670
        return $url;
671
    }
672
673
    /**
674
     * getAuthzIdP
675
     *
676
     * This function takes in the OAuth2 authorization URL and returns
677
     * the associated pretty-print name of the IdP.
678
     *
679
     * @param string $url The authorization URL of an OAuth2 Identity Provider.
680
     * @return string The name of the IdP.
681
     */
682
    public static function getAuthzIdP($url)
683
    {
684
        $idp = null;
685
        $urltoidp = array(
686
            'https://accounts.google.com/o/oauth2/auth' => 'Google',
687
            'https://github.com/login/oauth/authorize'  => 'GitHub',
688
            'https://orcid.org/oauth/authorize'         => 'ORCID',
689
        );
690
        if (array_key_exists($url, $urltoidp)) {
691
            $idp = $urltoidp[$url];
692
        }
693
        return $idp;
694
    }
695
696
    /**
697
     * saveUserToDataStore
698
     *
699
     * This function is called when a user logs on to save identity
700
     * information to the datastore. As it is used by both Shibboleth
701
     * and OpenID Identity Providers, some parameters passed in may
702
     * be blank (empty string). If the function verifies that the minimal
703
     * sets of parameters are valid, the dbservice servlet is called
704
     * to save the user info. Then various session variables are set
705
     * for use by the program later on. In case of error, an email
706
     * alert is sent showing the missing parameters.
707
     *
708
     * @param mixed $args Variable number of parameters, the same as those
709
     *        in DBService::$user_attrs
710
     */
711
    public static function saveUserToDataStore(...$args)
712
    {
713
        $dbs = new DBService();
714
715
        // Save the passed-in variables to the session for later use
716
        // (e.g., by the error handler in handleGotUser). Then get these
717
        // session variables into local vars for ease of use.
718
        static::setUserAttributeSessionVars(...$args);
719
720
        // This bit of trickery sets local variables from the PHP session
721
        // that was just populated, using the names in the $user_attrs array.
722
        foreach (DBService::$user_attrs as $value) {
723
            $$value = static::getSessionVar($value);
724
        }
725
726
        // For the new Google OAuth 2.0 endpoint, we want to keep the
727
        // old Google OpenID endpoint URL in the database (so user does
728
        // not get a new certificate subject DN). Change the idp
729
        // and idp_display_name to the old Google OpenID values.
730
        if (
731
            ($idp_display_name == 'Google+') ||
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp_display_name seems to be never defined.
Loading history...
732
            ($idp == static::getAuthzUrl('Google'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp seems to be never defined.
Loading history...
733
        ) {
734
            $idp_display_name = 'Google';
735
            $idp = 'https://www.google.com/accounts/o8/id';
736
        }
737
738
        // In the database, keep a consistent ProviderId format: only
739
        // allow 'http' (not 'https') and remove any 'www.' prefix.
740
        if ($loa == 'openid') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $loa seems to be never defined.
Loading history...
741
            $idp = preg_replace('%^https://(www\.)?%', 'http://', $idp);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp does not seem to be defined for all execution paths leading up to this point.
Loading history...
742
        }
743
744
        // Call the dbService to get the user using IdP attributes.
745
        $result = $dbs->getUser(
746
            $remote_user,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $remote_user seems to be never defined.
Loading history...
747
            $idp,
748
            $idp_display_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idp_display_name does not seem to be defined for all execution paths leading up to this point.
Loading history...
749
            $first_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $first_name seems to be never defined.
Loading history...
750
            $last_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last_name seems to be never defined.
Loading history...
751
            $display_name,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $display_name does not exist. Did you maybe mean $idp_display_name?
Loading history...
752
            $email,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $email seems to be never defined.
Loading history...
753
            $loa,
754
            $eppn,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eppn seems to be never defined.
Loading history...
755
            $eptid,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eptid seems to be never defined.
Loading history...
756
            $open_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $open_id seems to be never defined.
Loading history...
757
            $oidc,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $oidc seems to be never defined.
Loading history...
758
            $subject_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subject_id seems to be never defined.
Loading history...
759
            $pairwise_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pairwise_id seems to be never defined.
Loading history...
760
            $affiliation,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $affiliation seems to be never defined.
Loading history...
761
            $ou,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ou seems to be never defined.
Loading history...
762
            $member_of,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $member_of seems to be never defined.
Loading history...
763
            $acr,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $acr seems to be never defined.
Loading history...
764
            $amr,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $amr seems to be never defined.
Loading history...
765
            $entitlement,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $entitlement seems to be never defined.
Loading history...
766
            $itrustuin
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $itrustuin seems to be never defined.
Loading history...
767
        );
768
        if ($result) {
769
            static::setSessionVar('user_uid', $dbs->user_uid);
770
            static::setSessionVar('distinguished_name', $dbs->distinguished_name);
771
            static::setSessionVar('status', $dbs->status);
772
        } else {
773
            static::sendErrorAlert(
774
                'dbService Error',
775
                'Error calling dbservice action "getUser" in ' .
776
                'saveUserToDatastore() method.'
777
            );
778
            static::unsetSessionVar('user_uid');
779
            static::unsetSessionVar('distinguished_name');
780
            static::setSessionVar('status', DBService::$STATUS['STATUS_INTERNAL_ERROR']);
781
        }
782
783
        // If 'status' is not STATUS_OK*, then send an error email
784
        $status = static::getSessionVar('status');
785
        if ($status & 1) { // Bad status codes are odd
786
            // For missing parameter errors, log an error message
787
            if (
788
                $status ==
789
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']
790
            ) {
791
                $log = new Loggit();
792
                $log->error('STATUS_MISSING_PARAMETER_ERROR', true);
793
            }
794
795
            // For other dbservice errors OR for any error involving
796
            // LIGO (e.g., missing parameter error), send email alert.
797
            if (
798
                ($status !=
799
                    DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) ||
800
                (preg_match('/ligo\.org/', $idp))
801
            ) {
802
                $mailto = EMAIL_ALERTS;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\EMAIL_ALERTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
803
804
                // CIL-205 - Notify LIGO about IdP login errors.
805
                // Set DISABLE_LIGO_ALERTS to true in the top-level
806
                // config.php file to stop LIGO failures
807
                // from being sent to EMAIL_ALERTS, but still
808
                // sent to '[email protected]'.
809
                if (preg_match('/ligo\.org/', $idp)) {
810
                    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...
811
                        $mailto = '';
812
                    }
813
                    $mailto .= ((strlen($mailto) > 0) ? ',' : '') .
814
                        '[email protected]';
815
                }
816
817
                static::sendErrorAlert(
818
                    'Failure in ' .
819
                        (($loa == 'openid') ? '' : '/secure') . '/getuser/',
820
                    'Remote_User   = ' . ((strlen($remote_user) > 0) ?
821
                        $remote_user : '<MISSING>') . "\n" .
822
                    'IdP ID        = ' . ((strlen($idp) > 0) ?
823
                        $idp : '<MISSING>') . "\n" .
824
                    'IdP Name      = ' . ((strlen($idp_display_name) > 0) ?
825
                        $idp_display_name : '<MISSING>') . "\n" .
826
                    'First Name    = ' . ((strlen($first_name) > 0) ?
827
                        $first_name : '<MISSING>') . "\n" .
828
                    'Last Name     = ' . ((strlen($last_name) > 0) ?
829
                        $last_name : '<MISSING>') . "\n" .
830
                    'Display Name  = ' . ((strlen($display_name) > 0) ?
831
                        $display_name : '<MISSING>') . "\n" .
832
                    'Email Address = ' . ((strlen($email) > 0) ?
833
                        $email : '<MISSING>') . "\n" .
834
                    'LOA           = ' . ((strlen($loa) > 0) ?
835
                        $loa : '<MISSING>') . "\n" .
836
                    'ePPN          = ' . ((strlen($eppn) > 0) ?
837
                        $eppn : '<MISSING>') . "\n" .
838
                    'ePTID         = ' . ((strlen($eptid) > 0) ?
839
                        $eptid : '<MISSING>') . "\n" .
840
                    'OpenID ID     = ' . ((strlen($open_id) > 0) ?
841
                        $open_id : '<MISSING>') . "\n" .
842
                    'OIDC ID       = ' . ((strlen($oidc) > 0) ?
843
                        $oidc : '<MISSING>') . "\n" .
844
                    'Subject ID    = ' . ((strlen($subject_id) > 0) ?
845
                        $subject_id : '<MISSING>') . "\n" .
846
                    'Pairwise ID   = ' . ((strlen($pairwise_id) > 0) ?
847
                        $pairwise_id : '<MISSING>') . "\n" .
848
                    'Affiliation   = ' . ((strlen($affiliation) > 0) ?
849
                        $affiliation : '<MISSING>') . "\n" .
850
                    'OU            = ' . ((strlen($ou) > 0) ?
851
                        $ou : '<MISSING>') . "\n" .
852
                    'MemberOf      = ' . ((strlen($member_of) > 0) ?
853
                        $member_of : '<MISSING>') . "\n" .
854
                    'ACR           = ' . ((strlen($acr) > 0) ?
855
                        $acr : '<MISSING>') . "\n" .
856
                    'AMR           = ' . ((strlen($amr) > 0) ?
857
                        $amr : '<MISSING>') . "\n" .
858
                    'Entitlement   = ' . ((strlen($entitlement) > 0) ?
859
                        $entitlement : '<MISSING>') . "\n" .
860
                    'iTrustUIN     = ' . ((strlen($itrustuin) > 0) ?
861
                        $itrustuin : '<MISSING>') . "\n" .
862
                    'User UID      = ' . ((strlen(
863
                        $i = static::getSessionVar('user_uid')
864
                    ) > 0) ?  $i : '<MISSING>') . "\n" .
865
                    '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

865
                    'Status Code   = ' . (/** @scrutinizer ignore-type */ (strlen(
Loading history...
866
                        $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

866
                        /** @scrutinizer ignore-type */ $i = array_search(
Loading history...
867
                            $status,
868
                            DBService::$STATUS
869
                        )
870
                    ) > 0) ?  $i : '<MISSING>'),
871
                    $mailto
872
                );
873
            }
874
            static::unsetSessionVar('authntime');
875
        } else {
876
            // Success! We need to overwrite current session vars with values
877
            // returned by the DBService, e.g., in case attributes were set
878
            // previously but not this time. Skip 'idp' since the PHP code
879
            // transforms 'https://' to 'http://' for database consistency.
880
            // Also skip 'loa' since that is not saved in the database.
881
            foreach (DBService::$user_attrs as $value) {
882
                if (($value != 'idp') && ($value != 'loa')) {
883
                    static::setSessionVar($value, $dbs->$value);
884
                }
885
            }
886
        }
887
    }
888
889
    /**
890
     * setUserAttributeSessionVars
891
     *
892
     * This method is called by saveUserToDatastore to put the passsed-in
893
     * variables into the PHP session for later use.
894
     *
895
     * @param mixed $args Variable number of user attribute paramters
896
     *        ordered as shown in the DBService::$user_attrs array.
897
     */
898
    public static function setUserAttributeSessionVars(...$args)
899
    {
900
        // Loop through the list of user_attrs. First, unset any previous
901
        // value for the attribute, then set the passed-in attribute value.
902
        $numattrs = count(DBService::$user_attrs);
903
        $numargs = count($args);
904
        for ($i = 0; $i < $numattrs; $i++) {
905
            static::unsetSessionVar(DBService::$user_attrs[$i]);
906
            if ($i < $numargs) {
907
                static::setSessionVar(DBService::$user_attrs[$i], $args[$i]);
908
            }
909
        }
910
911
        static::setSessionVar('status', '0');
912
        static::setSessionVar('submit', static::getSessionVar('responsesubmit'));
913
        static::setSessionVar('authntime', time());
914
        static::unsetSessionVar('responsesubmit');
915
        static::getCsrf()->setCookieAndSession();
916
    }
917
918
    /**
919
     * unsetClientSessionVars
920
     *
921
     * This function removes all of the PHP session variables related to
922
     * the client session.
923
     */
924
    public static function unsetClientSessionVars()
925
    {
926
        static::unsetSessionVar('submit');
927
928
        // Specific to 'Download Certificate' page
929
        static::unsetSessionVar('p12');
930
        static::unsetSessionVar('p12lifetime');
931
        static::unsetSessionVar('p12multiplier');
932
933
        // Specific to OAuth 1.0a flow
934
        static::unsetSessionVar('portalstatus');
935
        static::unsetSessionVar('callbackuri');
936
        static::unsetSessionVar('successuri');
937
        static::unsetSessionVar('failureuri');
938
        static::unsetSessionVar('portalname');
939
        static::unsetSessionVar('tempcred');
940
941
        // Specific to OIDC flow
942
        static::unsetSessionVar('clientparams');
943
    }
944
945
    /**
946
     * unsetUserSessionVars
947
     *
948
     * This function removes all of the PHP session variables related to
949
     * the user's session.  This will force the user to log on (again)
950
     * with their IdP and call the 'getuser' script to repopulate the PHP
951
     * session.
952
     */
953
    public static function unsetUserSessionVars()
954
    {
955
        foreach (DBService::$user_attrs as $value) {
956
            static::unsetSessionVar($value);
957
        }
958
        static::unsetSessionVar('status');
959
        static::unsetSessionVar('user_uid');
960
        static::unsetSessionVar('distinguished_name');
961
        static::unsetSessionVar('authntime');
962
        static::unsetSessionVar('cilogon_skin');
963
    }
964
965
    /**
966
     * unsetAllUserSessionVars
967
     *
968
     * This is a convenience method to clear all session variables related
969
     * to the client and the user.
970
     */
971
    public static function unsetAllUserSessionVars()
972
    {
973
        static::unsetClientSessionVars();
974
        static::unsetUserSessionVars();
975
    }
976
977
    /**
978
     * verifySessionAndCall
979
     *
980
     * This function is a convenience method called by several cases in the
981
     * main 'switch' call at the top of the index.php file. I noticed
982
     * a pattern where verifyCurrentUserSession() was called to verify the
983
     * current user session. Upon success, one or two functions were called
984
     * to continue program, flow. Upon failure, cookies and session
985
     * variables were cleared, and the main Logon page was printed. This
986
     * function encapsulates that pattern. If the user's session is valid,
987
     * the passed-in $func is called, possibly with parameters passed in as
988
     * an array. The function returns true if the session is verified, so
989
     * that other functions may be called upon return.
990
     *
991
     * @param callable $func The function to call if the current session is
992
     *        successfully verified.
993
     * @param array $params (Optional) An array of parameters to pass to the
994
     *        function. Defaults to empty array, meaning zero parameters.
995
     */
996
    public static function verifySessionAndCall($func, $params = array())
997
    {
998
        $retval = false;
999
        if (Content::verifyCurrentUserSession()) { // Verify PHP session is valid
1000
            $retval = true;
1001
            call_user_func_array($func, $params);
1002
        } else {
1003
            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

1003
            /** @scrutinizer ignore-call */ 
1004
            printLogonPage(true); // Clear cookies and session vars too
Loading history...
1004
        }
1005
        return $retval;
1006
    }
1007
1008
    /**
1009
     * isEduGAINAndGetCert
1010
     *
1011
     * This function checks to see if the current session IdP is an
1012
     * eduGAIN IdP (i.e., not Registered By InCommon) and the IdP does not
1013
     * have both the REFEDS R&S and SIRTFI extensions in metadata. If so,
1014
     * check to see if the transaction could be used to fetch a
1015
     * certificate. (The only time the transaction is not used to fetch
1016
     * a cert is during OIDC without the 'getcert' scope.) If all that is
1017
     * true, then return true. Otherwise return false.
1018
     *
1019
     * @param string $idp (optional) The IdP entityID. If empty, read value
1020
     *        from PHP session.
1021
     * @param string $idp_display_name (optional) The IdP display name. If empty,
1022
     *        read value from PHP session.
1023
     * @return bool True if the current IdP is an eduGAIN IdP without
1024
     *         both REFEDS R&S and SIRTFI, AND the session could be
1025
     *         used to get a certificate.
1026
     */
1027
    public static function isEduGAINAndGetCert($idp = '', $idp_display_name = '')
1028
    {
1029
        $retval = false; // Assume not eduGAIN IdP and getcert
1030
1031
        // If $idp or $idp_display_name not passed in, get from current session.
1032
        if (strlen($idp) == 0) {
1033
            $idp = static::getSessionVar('idp');
1034
        }
1035
        if (strlen($idp_display_name) == 0) {
1036
            $idp_display_name = static::getSessionVar('idp_display_name');
1037
        }
1038
1039
        // Check if this was an OIDC transaction, and if the
1040
        // 'getcert' scope was requested.
1041
        $oidcscopegetcert = false;
1042
        $oidctrans = false;
1043
        $clientparams = json_decode(static::getSessionVar('clientparams'), true);
1044
        if (isset($clientparams['scope'])) {
1045
            $oidctrans = true;
1046
            if (
1047
                preg_match(
1048
                    '/edu.uiuc.ncsa.myproxy.getcert/',
1049
                    $clientparams['scope']
1050
                )
1051
            ) {
1052
                $oidcscopegetcert = true;
1053
            }
1054
        }
1055
1056
        // First, make sure $idp was set and is not an OAuth2 IdP.
1057
        $idplist = static::getIdpList();
1058
        if (
1059
            ((strlen($idp) > 0) &&
1060
            (strlen($idp_display_name) > 0) &&
1061
            (!in_array($idp_display_name, static::$oauth2idps))) &&
1062
                (
1063
                // Next, check for eduGAIN without REFEDS R&S and SIRTFI
1064
                ((!$idplist->isRegisteredByInCommon($idp)) &&
1065
                       ((!$idplist->isREFEDSRandS($idp)) ||
1066
                        (!$idplist->isSIRTFI($idp))
1067
                       )
1068
                ) &&
1069
                // Next, check if user could get X509 cert,
1070
                // i.e., OIDC getcert scope, or a non-OIDC
1071
                // transaction such as PKCS12, JWS, or OAuth 1.0a
1072
                ($oidcscopegetcert || !$oidctrans)
1073
                )
1074
        ) {
1075
            $retval = true;
1076
        }
1077
        return $retval;
1078
    }
1079
1080
    /**
1081
     * setPortalOrCookieVar
1082
     *
1083
     * This is a convenience function for a set of operations that is done
1084
     * a few times in Content.php. It first checks if the name of the portal
1085
     * in the PortalCookie is empty. If not, then it sets the PortalCookie
1086
     * key/value pair. Otherwise, it sets the 'normal' cookie key/value
1087
     * pair.
1088
     *
1089
     * @param PortalCookie $pc The PortalCookie to read/write. If the portal
1090
     *        name is empty, then use the 'normal' cookie instead.
1091
     * @param string $key The key of the PortalCookie or 'normal' cookie to
1092
     *        set.
1093
     * @param string $value The value to set for the $key.
1094
     * @param bool $save (optional) If set to true, attempt to write the
1095
     *        PortalCookie. Defaults to false.
1096
     */
1097
    public static function setPortalOrCookieVar($pc, $key, $value, $save = false)
1098
    {
1099
        $pn = $pc->getPortalName();
1100
        // If the portal name is valid, then set the PortalCookie key/value
1101
        if (strlen($pn) > 0) {
1102
            $pc->set($key, $value);
1103
            if ($save) {
1104
                $pc->write();
1105
            }
1106
        } else { // If portal name is not valid, then use the 'normal' cookie
1107
            if (strlen($value) > 0) {
1108
                Util::setCookieVar($key, $value);
1109
            } else { // If $value is empty, then UNset the 'normal' cookie
1110
                Util::unsetCookieVar($key);
1111
            }
1112
        }
1113
    }
1114
1115
    /**
1116
     * getOIDCClientParams
1117
     *
1118
     * This function addresses CIL-618 and reads OIDC client information
1119
     * directly from the database. It is a replacement for
1120
     * $dbs->getClient($clientparams['client_id']) which calls
1121
     * '/dbService?action=getClient&client_id=...'. This gives the PHP
1122
     * '/authorize' endpoint access to additional OIDC client parameters
1123
     * without having to rewrite the '/dbService?action=getClient' endpoint.
1124
     *
1125
     * @param array $clientparams An array of client parameters which gets
1126
     *              stored in the PHP session. The keys of the array are
1127
     *              the column names of the 'client' table in the 'ciloa2'
1128
     *              database, prefixed by 'client_'.
1129
     */
1130
    public static function getOIDCClientParams(&$clientparams)
1131
    {
1132
        $retval = false;
1133
        if (strlen(@$clientparams['client_id']) > 0) {
1134
            $dsn = array(
1135
                'phptype'  => 'mysqli',
1136
                '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...
1137
                '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...
1138
                'database' => 'ciloa2',
1139
                'hostspec' => 'localhost'
1140
            );
1141
1142
            $opts = array(
1143
                'persistent'  => true,
1144
                'portability' => DB_PORTABILITY_ALL
1145
            );
1146
1147
            $db = DB::connect($dsn, $opts);
1148
            if (!PEAR::isError($db)) {
1149
                $data = $db->getRow(
1150
                    'SELECT name,home_url,callback_uri,scopes from clients WHERE client_id = ?',
1151
                    array($clientparams['client_id']),
1152
                    DB_FETCHMODE_ASSOC
1153
                );
1154
                if (!DB::isError($data)) {
1155
                    if (!empty($data)) {
1156
                        foreach ($data as $key => $value) {
1157
                            $clientparams['client_' . $key] = $value;
1158
                        }
1159
                        $clientparams['clientstatus'] = DBService::$STATUS['STATUS_OK'];
1160
                        $retval = true;
1161
                    }
1162
                }
1163
                $db->disconnect();
1164
            }
1165
        }
1166
        return $retval;
1167
    }
1168
1169
    /**
1170
     * getMinMaxLifetimes
1171
     *
1172
     * This function checks the skin's configuration to see if either or
1173
     * both of minlifetime and maxlifetime in the specified config.xml
1174
     * block have been set. If not, default to minlifetime of 1 (hour) and
1175
     * the specified defaultmaxlifetime.
1176
     *
1177
     * @param string $section The XML section block from which to read the
1178
     *        minlifetime and maxlifetime values. Can be one of the
1179
     *        following: 'pkcs12' or 'delegate'.
1180
     * @param int $defaultmaxlifetime Default maxlifetime (in hours) for the
1181
     *        credential.
1182
     * @return array An array consisting of two entries: the minimum and
1183
     *         maximum lifetimes (in hours) for a credential.
1184
     */
1185
    public static function getMinMaxLifetimes($section, $defaultmaxlifetime)
1186
    {
1187
        $minlifetime = 1;    // Default minimum lifetime is 1 hour
1188
        $maxlifetime = $defaultmaxlifetime;
1189
        $skin = Util::getSkin();
1190
        $skinminlifetime = $skin->getConfigOption($section, 'minlifetime');
1191
        // Read the skin's minlifetime value from the specified section
1192
        if ((!is_null($skinminlifetime)) && ((int)$skinminlifetime > 0)) {
1193
            $minlifetime = max($minlifetime, (int)$skinminlifetime);
1194
            // Make sure $minlifetime is less than $maxlifetime;
1195
            $minlifetime = min($minlifetime, $maxlifetime);
1196
        }
1197
        // Read the skin's maxlifetime value from the specified section
1198
        $skinmaxlifetime = $skin->getConfigOption($section, 'maxlifetime');
1199
        if ((!is_null($skinmaxlifetime)) && ((int)$skinmaxlifetime) > 0) {
1200
            $maxlifetime = min($maxlifetime, (int)$skinmaxlifetime);
1201
            // Make sure $maxlifetime is greater than $minlifetime
1202
            $maxlifetime = max($minlifetime, $maxlifetime);
1203
        }
1204
1205
        return array($minlifetime, $maxlifetime);
1206
    }
1207
1208
    /**
1209
     * isLOASilver
1210
     *
1211
     * This function returns true if the 'loa' (level of assurance)
1212
     * should be http://incommonfederation.org/assurance/silver .
1213
     * As specified in CACC-238, this is when both of the following are true:
1214
     * (1) loa contains  https://refeds.org/assurance/profile/cappuccino
1215
     * (2) acr is either https://refeds.org/profile/sfa or
1216
     *                   https://refeds.org/profile/mfa
1217
     *
1218
     * @return bool True if level of assurance is 'silver'.
1219
     */
1220
    public static function isLOASilver()
1221
    {
1222
        $retval = false;
1223
        if (
1224
            (preg_match('%https://refeds.org/assurance/profile/cappuccino%', static::getSessionVar('loa'))) &&
1225
            (preg_match('%https://refeds.org/profile/[ms]fa%', static::getSessionVar('acr')))
1226
        ) {
1227
            $retval = true;
1228
        }
1229
        return $retval;
1230
    }
1231
1232
    /**
1233
     * getLOA
1234
     *
1235
     * This function is a bit of a hack. Once upon a time, the level of
1236
     * assurance (loa) was one of empty string (which implied 'basic
1237
     * CA'), 'openid' (which implied 'openid CA'), or
1238
     * 'http://incommonfederation.org/assurance/silver' (which implied
1239
     * 'silver CA'). Then things got more complex when the silver
1240
     * assurance was replaced by cappuccino (see CACC-238). But parts of the
1241
     * PHP code still depeneded on the InCommon silver string.
1242
     *
1243
     * This function transforms the assurance attribute asserted by an IdP
1244
     * (which is stored in the 'loa' session variable) into one of
1245
     * empty string (for 'basic CA'), 'openid', or
1246
     * 'http://incommonfederation.org/assurance/silver' for use by those
1247
     * PHP functions which expect the 'loa' in this format.
1248
     *
1249
     * @return string One of empty string, 'openid', or
1250
     *         'http://incommonfederation.org/assurance/silver'
1251
     */
1252
    public static function getLOA()
1253
    {
1254
        $retval = '';
1255
        if (static::isLOASilver()) {
1256
            $retval = 'http://incommonfederation.org/assurance/silver';
1257
        } else {
1258
            $retval = static::getSessionVar('loa');
1259
        }
1260
        return $retval;
1261
    }
1262
1263
    /**
1264
     * getLOAPort
1265
     *
1266
     * This function returns the port to be used for MyProxy based on the
1267
     * level of assurance.
1268
     *     Basic  CA = 7512
1269
     *     Silver CA = 7514
1270
     *     OpenID CA = 7516
1271
     *
1272
     * @return int The MyProxy port number to be used based on the 'level
1273
     *         of assurance' (basic, silver, openid).
1274
     */
1275
    public static function getLOAPort()
1276
    {
1277
        $port = 7512; // Basic
1278
        if (Util::isLOASilver()) {
1279
            $port = 7514;
1280
        } elseif (Util::getSessionVar('loa') == 'openid') {
1281
            $port = 7516;
1282
        }
1283
        return $port;
1284
    }
1285
1286
    /**
1287
     * getFirstAndLastName
1288
     *
1289
     * This function attempts to get the first and last name of a user
1290
     * extracted from the 'full name' (displayName) of the user.
1291
     * Simply pass in all name info (full, first, and last) and the
1292
     * function first tries to break up the full name into first/last.
1293
     * If this is not sufficient, the function checks first and last
1294
     * name. Finally, if either first or last is blank, the function
1295
     * duplicates first <=> last so both names have the same value.
1296
     * Note that even with all this, you still need to check if the
1297
     * returned (first,last) names are blank.
1298
     *
1299
     * @param string $full The 'full name' of the user
1300
     * @param string $first (Optional) The 'first name' of the user
1301
     * @param string $last (Optional) The 'last name' of the user
1302
     * @return array An array 'list(firstname,lastname)'
1303
     */
1304
    public static function getFirstAndLastName($full, $first = '', $last = '')
1305
    {
1306
        $firstname = '';
1307
        $lastname = '';
1308
1309
        # Try to split the incoming $full name into first and last names
1310
        if (strlen($full) > 0) {
1311
            if (preg_match('/,/', $full)) { // Split on comma if present
1312
                $names = preg_split('/,/', $full, 2);
1313
                $lastname =  trim(@$names[0]);
1314
                $firstname = trim(@$names[1]);
1315
            } else {
1316
                $names = preg_split('/\s+/', $full, 2);
1317
                $firstname = trim(@$names[0]);
1318
                $lastname =  trim(@$names[1]);
1319
            }
1320
        }
1321
1322
        # If either first or last name blank, then use incoming $first and $last
1323
        if (strlen($firstname) == 0) {
1324
            $firstname = $first;
1325
        }
1326
        if (strlen($lastname) == 0) {
1327
            $lastname = $last;
1328
        }
1329
1330
        # Finally, if only a single name, copy first name <=> last name
1331
        if (strlen($lastname) == 0) {
1332
            $lastname = $firstname;
1333
        }
1334
        if (strlen($firstname) == 0) {
1335
            $firstname = $lastname;
1336
        }
1337
1338
        # Return both names as an array (i.e., use list($first,last)=...)
1339
        return array($firstname,$lastname);
1340
    }
1341
1342
    /**
1343
     * cleanupPKCS12
1344
     *
1345
     * This function scans the DEFAULT_PKCS12_DIR and removes any
1346
     * directories (and contained files) that are older than 10 minutes.
1347
     * This function is used by the /cleancerts/ endpoint which can
1348
     * be called by a cronjob.
1349
     *
1350
     * @return int The number of PKCS12 dirs/files removed.
1351
     */
1352
    public static function cleanupPKCS12()
1353
    {
1354
        $numdel = 0;
1355
1356
        $pkcs12dir = DEFAULT_PKCS12_DIR;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\DEFAULT_PKCS12_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1357
        if (is_dir($pkcs12dir)) {
1358
            $files = scandir($pkcs12dir);
1359
            foreach ($files as $f) {
1360
                if (($f != '.') && ($f != '..')) {
1361
                    $tempdir = $pkcs12dir . $f;
1362
                    if ((filetype($tempdir) == 'dir') && ($f != '.git')) {
1363
                        if (time() > (600 + filemtime($tempdir))) {
1364
                            static::deleteDir($tempdir, true);
1365
                            $numdel++;
1366
                        }
1367
                    }
1368
                }
1369
            }
1370
        }
1371
        return $numdel;
1372
    }
1373
}
1374