Completed
Push — master ( 5c1146...9f11ab )
by Terrence
13:56
created

Util::sendErrorAlert()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 55
ccs 0
cts 42
cp 0
rs 8.9818
c 0
b 0
f 0
cc 4
nc 6
nop 3
crap 20

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 Config;
15
16
// Full path to the php.ini-style config file for the CILogon Service
17
define('CILOGON_INI_FILE', '/var/www/config/cilogon.ini');
18
19
/**
20
 * Util
21
 *
22
 * This class contains a bunch of static (class) utility
23
 * methods, for example getting and setting server environment
24
 * variables and handling cookies. See the header for each function for
25
 * detailed description.
26
 */
27
class Util
28
{
29
    /**
30
     * @var array $ini_array Read the cilogon.ini file into an array
31
     */
32
    public static $ini_array = null;
33
34
    /**
35
     * @var TimeIt $timeit Initialize by calling static::startTiming() in
36
     * init().
37
     */
38
    public static $timeit;
39
40
    /**
41
     * @var IdPList $idplist A 'global' IdpList object since dplist.xml is
42
     *      large and expensive to create multiple times.
43
     */
44
    public static $idplist = null;
45
46
    /**
47
     * @var CSRF $csrf A 'global' CSRF token object to set the CSRF cookie
48
     * and print the hidden CSRF form element. Needs to be set only once
49
     * to keep the same CSRF value through the session.
50
     */
51
    public static $csrf = null;
52
53
    /**
54
     * @var Skin $skin A 'global' Skin object for skin configuration.
55
     */
56
    public static $skin = null;
57
58
    /**
59
     * @var array $oauth2idps An array of OAuth2 Identity Providers.
60
     */
61
    public static $oauth2idps = ['Google', 'GitHub', 'ORCID'];
62
63
64
    /**
65
     * getIdPList
66
     *
67
     * This function initializes the class $idplist object (if not yet
68
     * created) and returns it. This allows for a single 'global'
69
     * $idplist to be used by other classes (since creating an IdPList
70
     * object is expensive).
71
     *
72
     * @return IdPList The class instantiated IdPList object.
73
     **/
74
    public static function getIdpList()
75
    {
76
        if (is_null(static::$idplist)) {
77
            static::$idplist = new IdpList();
78
        }
79
        return static::$idplist;
80
    }
81
82
    /**
83
     * getCsrf
84
     *
85
     * This function initializes the class $csrf object (if not yet
86
     * created) and returns it. This allows for a single 'global'
87
     * $csrf to be used by other classes (since we want the CSRV value
88
     * to be consistent for the current page load).
89
     *
90
     * @return CSRF The class instantiated CSRF object.
91
     */
92
    public static function getCsrf()
93
    {
94
        if (is_null(static::$csrf)) {
95
            static::$csrf = new CSRF();
96
        }
97
        return static::$csrf;
98
    }
99
100
    /**
101
     * getSkin
102
     *
103
     * This function initializes the class $skin object (if not yet
104
     * created) and returns it. This allows for a single 'global'
105
     * $skin to be used by other classes (since loading the skin is
106
     * potentially expensive).
107
     *
108
     * @return The class instantiated Skin object.
109
     */
110
    public static function getSkin()
111
    {
112
        if (is_null(static::$skin)) {
113
            static::$skin = new Skin();
114
        }
115
        return static::$skin;
116
    }
117
118
    /**
119
     * getConfigVar
120
     *
121
     * This function returns a sinle configuration vale from the
122
     * CILOGON_INI_FILE, or empty string if no such configuration
123
     * value is found in the file.
124
     *
125
     * @param string $config The config parameter to read from the
126
     *        cilogon.ini file.
127
     * @return string The value of the config parameter, or empty string
128
     *         if no such parameter found in config.ini.
129
     */
130
    public static function getConfigVar($config)
131
    {
132
        $retval = '';
133
        // Read in the config file into an array
134
        if (is_null(static::$ini_array)) {
135
            static::$ini_array = @parse_ini_file(CILOGON_INI_FILE);
136
        }
137
        if ((is_array(static::$ini_array)) &&
138
            (array_key_exists($config, static::$ini_array))) {
139
            $retval = static::$ini_array[$config];
140
        }
141
        return $retval;
142
    }
143
144
    /**
145
     * startTiming
146
     *
147
     * This function initializes the class variable $timeit which is
148
     * used for timing/benchmarking purposes.
149
     */
150
    public static function startTiming()
151
    {
152
        static::$timeit = new TimeIt(TimeIt::DEFAULTFILENAME, true);
153
    }
154
155
    /**
156
     * getServerVar
157
     *
158
     * This function queries a given $_SERVER variable (which is set
159
     * by the Apache server) and returns the value.
160
     *
161
     * @param string $serv The $_SERVER variable to query.
162
     * @return string The value of the $_SERVER variable or empty string
163
     *         if that variable is not set.
164
     */
165
    public static function getServerVar($serv)
166
    {
167
        $retval = '';
168
        if (isset($_SERVER[$serv])) {
169
            $retval = $_SERVER[$serv];
170
        }
171
        return $retval;
172
    }
173
174
    /**
175
     * getGetVar
176
     *
177
     * This function queries a given $_GET parameter (which is set in
178
     * the URL via a '?parameter=value' parameter) and returns the
179
     * value.
180
     *
181
     * @param string $get The $_GET variable to query.
182
     * @return string The value of the $_GET variable or empty string if
183
     *         that variable is not set.
184
     */
185
    public static function getGetVar($get)
186
    {
187
        $retval = '';
188
        if (isset($_GET[$get])) {
189
            $retval = $_GET[$get];
190
        }
191
        return $retval;
192
    }
193
194
    /**
195
     * getPostVar
196
     *
197
     * This function queries a given $_POST variable (which is set when
198
     * the user submits a form, for example) and returns the value.
199
     *
200
     * @param string $post The $_POST variable to query.
201
     * @return string The value of the $_POST variable or empty string if
202
     *         that variable is not set.
203
     */
204
    public static function getPostVar($post)
205
    {
206
        $retval = '';
207
        if (isset($_POST[$post])) {
208
            $retval = $_POST[$post];
209
        }
210
        return $retval;
211
    }
212
213
    /**
214
     * getCookieVar
215
     *
216
     * This function returns the value of a given cookie.
217
     *
218
     * @param string $cookie he $_COOKIE variable to query.
219
     * @return string The value of the $_COOKIE variable or empty string
220
     *         if that variable is not set.
221
     */
222
    public static function getCookieVar($cookie)
223
    {
224
        $retval = '';
225
        if (isset($_COOKIE[$cookie])) {
226
            $retval = $_COOKIE[$cookie];
227
        }
228
        return $retval;
229
    }
230
231
    /**
232
     * setCookieVar
233
     *
234
     * This function sets a cookie.
235
     *
236
     * @param string $cookie The name of the cookie to set.
237
     * @param string $value (Optional) The value to set for the cookie.
238
     *        Defaults to empty string.
239
     * @param int $exp The future expiration time (in seconds) of the
240
     *        cookie. Defaults to 1 year from now. If set to 0,
241
     *        the cookie expires at the end of the session.
242
     */
243
    public static function setCookieVar($cookie, $value = '', $exp = 31536000)
244
    {
245
        if ($exp > 0) {
246
            $exp += time();
247
        }
248
        setcookie($cookie, $value, $exp, '/', '.'.static::getDN(), true);
249
        $_COOKIE[$cookie] = $value;
250
    }
251
252
    /**
253
     * unsetCookieVar
254
     *
255
     * This function unsets a cookie. Strictly speaking, the cookie is
256
     * not removed, rather it is set to an empty value with an expired
257
     * time.
258
     *
259
     * @param string $cookie The name of the cookie to unset (delete).
260
     */
261
    public static function unsetCookieVar($cookie)
262
    {
263
        setcookie($cookie, '', 1, '/', '.'.static::getDN(), true);
264
        unset($_COOKIE[$cookie]);
265
    }
266
267
    /**
268
     * getPortalOrNormalCookieVar
269
     *
270
     * This is a convenience function which first checks if there is a
271
     * OAuth 1.0a ('delegate') or OIDC ('authorize') session active.
272
     * If so, it attempts to get the requested cookie from the
273
     * associated portalcookie. If there is not an OAuth/OIDC session
274
     * active, it looks for a 'normal' cookie. If you need a
275
     * portalcookie object to do multiple get/set method calls from
276
     * one function, it is probably better NOT to use this method since
277
     * creating the portalcookie object is potentially expensive.
278
     *
279
     * @param string $cookie The name of the cookie to get.
280
     * @return string The cookie value from either the portalcookie
281
     *         (in the case of an active OAuth session) or the
282
     *         'normal' cookie. Return empty string if no matching
283
     *         cookie in either place.
284
     */
285
    public static function getPortalOrNormalCookieVar($cookie)
286
    {
287
        $retval = '';
0 ignored issues
show
Unused Code introduced by
$retval is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
288
        $pc = new PortalCookie();
289
        $pn = $pc->getPortalName();
290
        if (strlen($pn) > 0) {
291
            $retval = $pc->get($cookie);
292
        } else {
293
            $retval = static::getCookieVar($cookie);
294
        }
295
        return $retval;
296
    }
297
298
    /**
299
     * getSessionVar
300
     *
301
     * This function returns the value of a given PHP Session variable.
302
     *
303
     * @param string $sess The $_SESSION variable to query.
304
     * @return string The value of the $_SESSION variable or empty string
305
     *         if that variable is not set.
306
     */
307
    public static function getSessionVar($sess)
308
    {
309
        $retval = '';
310
        if (isset($_SESSION[$sess])) {
311
            $retval = $_SESSION[$sess];
312
        }
313
        return $retval;
314
    }
315
316
    /**
317
     * setSessionVar
318
     *
319
     * This function can set or unset a given PHP session variable.
320
     * The first parameter is the PHP session variable to set/unset.
321
     * If the second parameter is the empty string, then the session
322
     * variable is unset.  Otherwise, the session variable is set to
323
     * the second parameter.  The function returns true if the session
324
     * variable was set to a non-empty value, false otherwise.
325
     * Normally, the return value can be ignored.
326
     *
327
     * @param string $key The name of the PHP session variable to set
328
     *        (or unset).
329
     * @param string $value (Optional) The value of the PHP session variable
330
     *        (to set), or empty string (to unset). Defaults to empty
331
     *        string (implies unset the session variable).
332
     * @return bool True if the PHP session variable was set to a
333
     *         non-empty string, false if variable was unset or if
334
     *         the specified session variable was not previously set.
335
     */
336
    public static function setSessionVar($key, $value = '')
337
    {
338
        $retval = false;  // Assume we want to unset the session variable
339
        if (strlen($key) > 0) {  // Make sure session var name was passed in
340
            if (strlen($value) > 0) {
341
                $_SESSION[$key] = $value;
342
                $retval = true;
343
            } else {
344
                static::unsetSessionVar($key);
345
            }
346
        }
347
        return $retval;
348
    }
349
350
    /**
351
     * unsetSessionVar
352
     *
353
     * This function clears the given PHP session variable by first
354
     * setting it to null and then unsetting it entirely.
355
     *
356
     * @param string $sess The $_SESSION variable to erase.
357
     */
358
    public static function unsetSessionVar($sess)
359
    {
360
        if (isset($_SESSION[$sess])) {
361
            $_SESSION[$sess] = null;
362
            unset($_SESSION[$sess]);
363
        }
364
    }
365
366
    /**
367
     * removeShibCookies
368
     *
369
     * This function removes all '_shib*' cookies currently in the
370
     * user's browser session. In effect, this logs the user out of
371
     * any IdP. Note that you must call this before you output any
372
     * HTML. Strictly speaking, the cookies are not removed, rather
373
     * they are set to empty values with expired times.
374
     */
375
    public static function removeShibCookies()
376
    {
377
        while (list($key, $val) = each($_COOKIE)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $val is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
378
            if (strncmp($key, '_shib', strlen('_shib')) == 0) {
379
                static::unsetCookieVar($key);
380
            }
381
        }
382
    }
383
384
    /**
385
     * startPHPSession
386
     *
387
     * This function starts a secure PHP session and should be called
388
     * at the beginning of each script before any HTML is output.  It
389
     * does a trick of setting a 'lastaccess' time so that the
390
     * $_SESSION variable does not expire without warning.
391
     *
392
     * @param string $storetype (Optional) Storage location of the PHP
393
     *        session data, one of 'file' or 'mysql'. Defaults to null,
394
     *        which means use the value of storage.phpsessions from the
395
     *        cilogon.ini config file, or 'file' if no such
396
     *        parameter configured.
397
     */
398
    public static function startPHPSession($storetype = null)
0 ignored issues
show
Unused Code introduced by
The parameter $storetype is not used and could be removed.

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

Loading history...
399
    {
400
        // No parameter given? Use the value read in from cilogon.ini file.
401
        // If storage.phpsessions == 'mysql', create a sessionmgr().
402
        $storetype = static::getConfigVar('storage.phpsessions');
403
404
        if (preg_match('/^mysql/', $storetype)) {
405
            $sessionmgr = new SessionMgr();
0 ignored issues
show
Unused Code introduced by
$sessionmgr is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
406
        }
407
408
        ini_set('session.cookie_secure', true);
409
        ini_set('session.cookie_domain', '.'.static::getDN());
410
        session_start();
411
        if ((!isset($_SESSION['lastaccess']) ||
412
            (time() - $_SESSION['lastaccess']) > 60)) {
413
            $_SESSION['lastaccess'] = time();
414
        }
415
    }
416
417
    /**
418
     * getScriptDir
419
     *
420
     * This function returns the directory (or full url) of the script
421
     * that is currently running.  The returned directory/url is
422
     * terminated by a '/' character (unless the second parameter is
423
     * set to true). This function is useful for those scripts named
424
     * index.php where we don't want to actually see 'index.php' in the
425
     * address bar (again, unless the second parameter is set to true).
426
     *
427
     * @param bool $prependhttp (Optional) Boolean to prepend 'http(s)://' to
428
     *        the script name. Defaults to false.
429
     * @param bool $stripfile (Optional) Boolean to strip off the trailing
430
     *        filename (e.g. index.php) from the path.
431
     *        Defaults to true (i.e., defaults to directory
432
     *        only without the trailing filename).
433
     * @return string The directory or url of the current script, with or
434
     *         without the trailing .php filename.
435
     */
436
    public static function getScriptDir($prependhttp = false, $stripfile = true)
437
    {
438
        $retval = static::getServerVar('SCRIPT_NAME');
439
        if ($stripfile) {
440
            $retval = dirname($retval);
441
        }
442
        if ($retval == '.') {
443
            $retval = '';
444
        }
445
        if ((strlen($retval) == 0) ||
446
            ($stripfile && ($retval[strlen($retval)-1] != '/'))) {
447
            $retval .= '/';  // Append a slash if necessary
448
        }
449
        if ($prependhttp) {  // Prepend http(s)://hostname
450
            $retval = 'http' .
451
                      ((strtolower(static::getServerVar('HTTPS')) == 'on')?'s':'') .
452
                      '://' . static::getServerVar('HTTP_HOST') . $retval;
453
        }
454
        return $retval;
455
    }
456
457
    /**
458
     * readArrayFromFile
459
     *
460
     * This function reads in the contents of a file into an array. It
461
     * is assumed that the file contains lines of the form:
462
     *     key value
463
     * where 'key' and 'value' are separated by whitespace.  The 'key'
464
     * portion of the string may not contain any whitespace, but the
465
     * 'value' part of the line may contain whitespace. Any empty lines
466
     * or lines starting with '#' (comments, without leading spaces)
467
     * in the file are skipped.  Note that this assumes that each 'key'
468
     * in the file is unique.  If there is any problem reading the
469
     * file, the resulting array will be empty.
470
     *
471
     * @param string $filename The name of the file to read.
472
     * @return array An array containing the contents of the file.
473
     */
474
    public static function readArrayFromFile($filename)
475
    {
476
        $retarray = array();
477
        if (is_readable($filename)) {
478
            $lines = file(
479
                $filename,
480
                FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES
481
            );
482
            foreach ($lines as $line) {
483
                if (substr($line, 0, 1) != '#') { // Skip '#' comment lines
484
                    $values = preg_split('/\s+/', $line, 2);
485
                    $retarray[$values[0]] = @$values[1];
486
                }
487
            }
488
        }
489
490
        return $retarray;
491
    }
492
493
    /**
494
     * writeArrayToFile
495
     *
496
     * This funtion writes an array (with key=>value pairs) to a file,
497
     * each line will be of the form:
498
     *     key value
499
     * The 'key' and 'value' strings are separated by a space. Note
500
     * that a 'key' may not contain any whitespace (e.g. tabs), but a
501
     * 'value' may contain whitespace. To be super safe, the array is
502
     * first written to a temporary file, which is then renamed to the
503
     * final desired filename.
504
     *
505
     * @param string $filename The name of the file to write.
506
     * @param array $thearray The array to be written to the file.
507
     * @return bool True if successfully wrote file, false otherwise.
508
     */
509
    public static function writeArrayToFile($filename, $thearray)
510
    {
511
        $retval = false;  // Assume write failed
512
        $tmpfnmae = tempnam('/tmp', 'ARR');
0 ignored issues
show
Unused Code introduced by
$tmpfnmae is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
513
        if ($fh = fopen($tmpfname, 'w')) {
514
            if (flock($fh, LOCK_EX)) {
515
                foreach ($thearray as $key => $value) {
516
                    fwrite($fh, "$key $value\n");
517
                }
518
                flock($fh, LOCK_UN);
519
            }
520
            fclose($fh);
521
            if (@rename($tmpfname, $filename)) {
522
                $retval = true;
523
            } else {
524
                @unlink($tmpfname);
0 ignored issues
show
Bug introduced by
The variable $tmpfname does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
525
            }
526
        }
527
528
        return $retval;
529
    }
530
531
    /**
532
     * parseGridShibConf
533
     *
534
     * This function parses the gridshib-ca.conf file and returns an
535
     * array containing the various options. It uses the PHP
536
     * PEAR::Config package to parse the config file. The
537
     * gridshib-ca.conf file is MOSTLY an Apache-style config file.
538
     * However, each option has an extra ' = ' prepended, so you will
539
     * need to strip these off each config option. For example, to get
540
     * the 'MaximumCredLifetime' value which is in the 'CA' section,
541
     * you would do the following:
542
     *     $gridshibconf = Util::parseGridShibConf();
543
     *     $life = preg_replace('%^\s*=\s*%','',
544
     *             $gridshibconf['root']['CA']['MaximumCredLifetime']);
545
     *
546
     * @param string $conffile (Optional) Full path location of
547
     *        gridshib-ca.conf file. Defaults to
548
     *        '/usr/local/gridshib-ca/conf/gridshib-ca.conf'.
549
     * @return array An array containing the various configuration
550
     *         parameters in the gridshib-ca.conf file.
551
     */
552
    public static function parseGridShibConf(
553
        $conffile = '/usr/local/gridshib-ca/conf/gridshib-ca.conf'
554
    ) {
555
        $conf = new Config;
556
        $root = $conf->parseConfig($conffile, 'Apache');
557
        $gridshibconf = array();
558
        if (!(PEAR::isError($root))) {
559
            $gridshibconf = $root->toArray();
560
        }
561
        return $gridshibconf;
562
    }
563
564
    /**
565
     * tempDir
566
     *
567
     * This function creates a temporary subdirectory within the
568
     * specified subdirectory. The new directory name is composed of
569
     * 16 hexadecimal letters, plus any prefix if you specify one. The
570
     * full path of the the newly created directory is returned.
571
     *
572
     * @param string $dir The full path to the containing directory.
573
     * @param string $prefix (Optional) A prefix for the new temporary
574
     *        directory. Defaults to empty string.
575
     * @param int $mode (Optional) Access permissions for the new
576
     *        temporary directory. Defaults to 0775.
577
     * @return string Full path to the newly created temporary directory.
578
     */
579
    public static function tempDir($dir, $prefix = '', $mode = 0775)
580
    {
581
        if (substr($dir, -1) != '/') {
582
            $dir .= '/';
583
        }
584
585
        $path = '';
0 ignored issues
show
Unused Code introduced by
$path is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
586
        do {
587
            $path = $dir . $prefix . sprintf("%08X%08X", mt_rand(), mt_rand());
588
        } while (!mkdir($path, $mode, true));
589
590
        return $path;
591
    }
592
593
    /**
594
     * deleteDir
595
     *
596
     * This function deletes a directory and all of its contents.
597
     *
598
     * @param string $dir The (possibly non-empty) directory to delete.
599
     * @param bool $shred (Optional) Shred the file before deleting?
600
     *        Defaults to false.
601
     */
602
    public static function deleteDir($dir, $shred = false)
603
    {
604
        if (is_dir($dir)) {
605
            $objects = scandir($dir);
606
            foreach ($objects as $object) {
607
                if ($object != "." && $object != "..") {
608
                    if (filetype($dir."/".$object) == "dir") {
609
                        static::deleteDir($dir."/".$object);
610
                    } else {
611
                        if ($shred) {
612
                            @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 here. This can introduce security issues, and is generally not recommended.

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...
613
                        } else {
614
                            @unlink($dir."/".$object);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
615
                        }
616
                    }
617
                }
618
            }
619
            reset($objects);
620
            @rmdir($dir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
621
        }
622
    }
623
624
    /**
625
     * htmlent
626
     *
627
     * This method is necessary since htmlentities() does not seem to
628
     * obey the default arguments as documented in the PHP manual, and
629
     * instead encodes accented characters incorrectly. By specifying
630
     * the flags and encoding, the problem is solved.
631
     *
632
     * @param string $str : A string to process with htmlentities().
633
     * @return string The input string processed by htmlentities with
634
     *         specific options.
635
     */
636
    public static function htmlent($str)
637
    {
638
        return htmlentities($str, ENT_COMPAT|ENT_HTML401, 'UTF-8');
639
    }
640
641
    /**
642
     * sendErrorAlert
643
     *
644
     * Use this function to send an error message. The $summary should
645
     * be a short description of the error since it is placed in the
646
     * subject of the email. Put a more verbose description of the
647
     * error in the $detail parameter. Any session variables available
648
     * are appended to the body of the message.
649
     *
650
     * @param string $summary A brief summary of the error (in email subject)
651
     * @param string $detail A detailed description of the error (in the
652
     *        email body)
653
     * @param string $mailto (Optional) The destination email address.
654
     *        Defaults to '[email protected]'.
655
     */
656
    public static function sendErrorAlert(
657
        $summary,
658
        $detail,
659
        $mailto = '[email protected]'
660
    ) {
661
        $sessionvars = array(
662
            'idp'          => 'IdP ID',
663
            'idpname'      => 'IdP Name',
664
            'uid'          => 'Database UID',
665
            'dn'           => 'Cert DN',
666
            'firstname'    => 'First Name',
667
            'lastname'     => 'Last Name',
668
            'displayname'  => 'Display Name',
669
            'ePPN'         => 'ePPN',
670
            'ePTID'        => 'ePTID',
671
            'openID'       => 'OpenID ID',
672
            'oidcID'       => 'OIDC ID',
673
            'loa'          => 'LOA',
674
            'affiliation'  => 'Affiliation',
675
            'ou'           => 'OU',
676
            'memberof'     => 'MemberOf',
677
            'acr'          => 'AuthnContextClassRef',
678
            'entitlement'  => 'Entitlement',
679
            'cilogon_skin' => 'Skin Name',
680
            'twofactor'    => 'Two-Factor',
681
            'authntime'    => 'Authn Time'
682
        );
683
684
        $remoteaddr = static::getServerVar('REMOTE_ADDR');
685
        $remotehost = gethostbyaddr($remoteaddr);
686
        $mailfrom = 'From: [email protected]' . "\r\n" .
687
                    'X-Mailer: PHP/' . phpversion();
688
        $mailsubj = 'CILogon Service on ' . php_uname('n') .
689
                    ' - ' . $summary;
690
        $mailmsg  = '
691
CILogon Service - ' . $summary . '
692
-----------------------------------------------------------
693
' . $detail . '
694
695
Session Variables
696
-----------------
697
Timestamp     = ' . date(DATE_ATOM) . '
698
Server Host   = ' . static::getHN() . '
699
Remote Address= ' . $remoteaddr . '
700
' . (($remotehost !== false) ? "Remote Host   = $remotehost" : '') . '
701
';
702
703
        foreach ($sessionvars as $svar => $sname) {
704
            if (strlen($val = static::getSessionVar($svar)) > 0) {
705
                $mailmsg .= sprintf("%-14s= %s\n", $sname, $val);
706
            }
707
        }
708
709
        mail($mailto, $mailsubj, $mailmsg, $mailfrom);
710
    }
711
712
    /**
713
     * getFirstAndLastName
714
     *
715
     * This function attempts to get the first and last name of a user
716
     * extracted from the 'full name' (displayName) of the user.
717
     * Simply pass in all name info (full, first, and last) and the
718
     * function first tries to break up the full name into first/last.
719
     * If this is not sufficient, the function checks first and last
720
     * name. Finally, if either first or last is blank, the function
721
     * duplicates first <=> last so both names have the same value.
722
     * Note that even with all this, you still need to check if the
723
     * returned (first,last) names are blank.
724
     *
725
     * @param string $full The 'full name' of the user
726
     * @param string $first (Optional) The 'first name' of the user
727
     * @param string $last (Optional) The 'last name' of the user
728
     * @return array An array 'list(firstname,lastname)'
729
     */
730
    public static function getFirstAndLastName($full, $first = '', $last = '')
731
    {
732
        $firstname = '';
733
        $lastname = '';
734
735
        # Try to split the incoming $full name into first and last names
736
        if (strlen($full) > 0) {
737
            $names = preg_split('/\s+/', $full, 2);
738
            $firstname = @$names[0];
739
            $lastname =  @$names[1];
740
        }
741
742
        # If either first or last name blank, then use incoming $first and $last
743
        if (strlen($firstname) == 0) {
744
            $firstname = $first;
745
        }
746
        if (strlen($lastname) == 0) {
747
            $lastname = $last;
748
        }
749
750
        # Finally, if only a single name, copy first name <=> last name
751
        if (strlen($lastname) == 0) {
752
            $lastname = $firstname;
753
        }
754
        if (strlen($firstname) == 0) {
755
            $firstname = $lastname;
756
        }
757
758
        # Return both names as an array (i.e., use list($first,last)=...)
759
        return array($firstname,$lastname);
760
    }
761
762
    /**
763
     * getHN
764
     *
765
     * This function calculates and returns the 'hostname' for the
766
     * server. It first checks HTTP_HOST. If not set, it returns
767
     * 'cilogon.org'. This is needed by command line scripts.
768
     *
769
     * @return string The 'Hostname' for the web server.
770
     */
771
    public static function getHN()
772
    {
773
        $thehostname = static::getServerVar('HTTP_HOST');
774
        if (strlen($thehostname) == 0) {
775
            $thehostname = 'cilogon.org';
776
        }
777
        return $thehostname;
778
    }
779
780
    /**
781
     * getDN
782
     *
783
     * This function calculates and returns the 'domainname' for the
784
     * server. It uses the hostname value calculated by getHN() and
785
     * uses the last two segments.
786
     *
787
     * @return string The 'Domainname' for the web server.
788
     */
789
    public static function getDN()
790
    {
791
        $thedomainname = static::getHN();
792
        if (preg_match('/[^\.]+\.[^\.]+$/', $thedomainname, $matches)) {
793
            $thedomainname = $matches[0];
794
        }
795
        return $thedomainname;
796
    }
797
798
    /**
799
     * getAuthzUrl
800
     *
801
     * This funtion takes in the name of an IdP (e.g., 'Google') and
802
     * returns the assoicated OAuth2 authorization URL.
803
     *
804
     * @param string $idp The name of an OAuth2 Identity Provider.
805
     * @return string The authorization URL for the given IdP.
806
     */
807
    public static function getAuthzUrl($idp)
808
    {
809
        $url = null;
810
        $idptourl = array(
811
            'Google' => 'https://accounts.google.com/o/oauth2/auth',
812
            'GitHub' => 'https://github.com/login/oauth/authorize',
813
            'ORCID'  => 'https://orcid.org/oauth/authorize',
814
        );
815
        if (array_key_exists($idp, $idptourl)) {
816
            $url = $idptourl[$idp];
817
        }
818
        return $url;
819
    }
820
821
    /**
822
     * getAuthzIdP
823
     *
824
     * This function takes in the OAuth2 authorization URL and returns
825
     * the associated pretty-print name of the IdP.
826
     *
827
     * @param string $url The authorization URL of an OAuth2 Identity Provider.
828
     * @return string The name of the IdP.
829
     */
830
    public static function getAuthzIdP($url)
831
    {
832
        $idp = null;
833
        $urltoidp = array(
834
            'https://accounts.google.com/o/oauth2/auth' => 'Google',
835
            'https://github.com/login/oauth/authorize'  => 'GitHub',
836
            'https://orcid.org/oauth/authorize'         => 'ORCID',
837
        );
838
        if (array_key_exists($url, $urltoidp)) {
839
            $idp = $urltoidp[$url];
840
        }
841
        return $idp;
842
    }
843
844
    /**
845
     * saveUserToDataStore
846
     *
847
     * This function is called when a user logs on to save identity
848
     * information to the datastore. As it is used by both Shibboleth
849
     * and OpenID Identity Providers, some parameters passed in may
850
     * be blank (empty string). The function verifies that the minimal
851
     * sets of parameters are valid, the dbservice servlet is called
852
     * to save the user info. Then various session variables are set
853
     * for use by the program later on. In case of error, an email
854
     * alert is sent showing the missing parameters.
855
     *
856
     * @param string $remoteuser The REMOTE_USER from HTTP headers
857
     * @param string $providerId The provider IdP Identifier / URL endpoint
858
     * @param string providerName The pretty print provider IdP name
859
     * @param string $firstname The user's first name
860
     * @param string $lastname The user's last name
861
     * @param string $displayname The user's display name
862
     * @param string $emailaddr The user's email address
863
     * @param string $loa The level of assurance (e.g., openid/basic/silver)
864
     * @param string $eppn (optional) User's ePPN (for SAML IdPs)
865
     * @param string $eptid (optional) User's ePTID (for SAML IdPs)
866
     * @param string $openidid (optional) User's OpenID 2.0 Identifier
867
     * @param string $oidcid (optional) User's OpenID Connect Identifier
868
     * @param string $affiliation (optional) User's affiliation
869
     * @param string $ou (optional) User's organizational unit (OU)
870
     * @param string $memberof (optional) User's isMemberOf group info
871
     * @param string $acr (optional) Authentication Context Class Ref
872
     * @param string $entitlement (optional) User's entitlement
873
     */
874
    public static function saveUserToDataStore(
875
        $remoteuser,
876
        $providerId,
877
        $providerName,
878
        $firstname,
879
        $lastname,
880
        $displayname,
881
        $emailaddr,
882
        $loa,
883
        $eppn = '',
884
        $eptid = '',
885
        $openidid = '',
886
        $oidcid = '',
887
        $affiliation = '',
888
        $ou = '',
889
        $memberof = '',
890
        $acr = '',
891
        $entitlement = ''
892
    ) {
893
        $dbs = new DBService();
894
895
        // Keep original values of providerName and providerId
896
        $databaseProviderName = $providerName;
897
        $databaseProviderId   = $providerId;
898
899
        // Save the passed-in variables to the session for later use
900
        // (e.g., by the error handler in handleGotUser).
901
        static::setSessionVar('firstname', $firstname);
902
        static::setSessionVar('lastname', $lastname);
903
        static::setSessionVar('displayname', $displayname);
904
        static::setSessionvar('emailaddr', $emailaddr);
905
        static::setSessionVar('loa', $loa);
906
        static::setSessionVar('ePPN', $eppn);
907
        static::setSessionVar('ePTID', $eptid);
908
        static::setSessionVar('openidID', $openidid);
909
        static::setSessionVar('oidcID', $oidcid);
910
        static::setSessionVar('affiliation', $affiliation);
911
        static::setSessionVar('ou', $ou);
912
        static::setSessionVar('memberof', $memberof);
913
        static::setSessionVar('acr', $acr);
914
        static::setSessionVar('entitlement', $entitlement);
915
        static::setSessionVar('idp', $providerId); // Enable error message
916
        static::setSessionVar('idpname', $providerName); // Enable check for Google
917
        static::setSessionVar('submit', static::getSessionVar('responsesubmit'));
918
919
        // CACC-238 - Set loa to "silver" if the following are true:
920
        // (1) loa contains  https://refeds.org/assurance/profile/cappuccino
921
        // (2) acr is either https://refeds.org/profile/sfa or
922
        //                   https://refeds.org/profile/mfa
923
        if ((preg_match('%https://refeds.org/assurance/profile/cappuccino%', $loa)) &&
924
            (preg_match('%https://refeds.org/profile/[ms]fa%', $acr))) {
925
            $loa = 'http://incommonfederation.org/assurance/silver';
926
            static::setSessionVar('loa', $loa);
927
        }
928
929
        // Make sure parameters are not empty strings, and email is valid
930
        // Must have at least one of remoteuser/eppn/eptid/openidid/oidcid
931
        if (((strlen($remoteuser) > 0) ||
932
               (strlen($eppn) > 0) ||
933
               (strlen($eptid) > 0) ||
934
               (strlen($openidid) > 0) ||
935
               (strlen($oidcid) > 0)) &&
936
            (strlen($databaseProviderId) > 0) &&
937
            (strlen($databaseProviderName) > 0)  &&
938
            (strlen($firstname) > 0) &&
939
            (strlen($lastname) > 0) &&
940
            (strlen($emailaddr) > 0) &&
941
            (filter_var($emailaddr, FILTER_VALIDATE_EMAIL))) {
942
            // For the new Google OAuth 2.0 endpoint, we want to keep the
943
            // old Google OpenID endpoint URL in the database (so user does
944
            // not get a new certificate subject DN). Change the providerId
945
            // and providerName to the old Google OpenID values.
946
            if (($databaseProviderName == 'Google+') ||
947
                ($databaseProviderId == static::getAuthzUrl('Google'))) {
948
                $databaseProviderName = 'Google';
949
                $databaseProviderId = 'https://www.google.com/accounts/o8/id';
950
            }
951
952
            // In the database, keep a consistent ProviderId format: only
953
            // allow 'http' (not 'https') and remove any 'www.' prefix.
954
            if ($loa == 'openid') {
955
                $databaseProviderId = preg_replace(
956
                    '%^https://(www\.)?%',
957
                    'http://',
958
                    $databaseProviderId
959
                );
960
            }
961
962
            $result = $dbs->getUser(
963
                $remoteuser,
964
                $databaseProviderId,
965
                $databaseProviderName,
966
                $firstname,
967
                $lastname,
968
                $displayname,
969
                $emailaddr,
970
                $eppn,
971
                $eptid,
972
                $openidid,
973
                $oidcid,
974
                $affiliation,
975
                $ou,
976
                $memberof,
977
                $acr,
978
                $entitlement
979
            );
980
            static::setSessionVar('uid', $dbs->user_uid);
981
            static::setSessionVar('dn', $dbs->distinguished_name);
982
            static::setSessionVar('twofactor', $dbs->two_factor);
983
            static::setSessionVar('status', $dbs->status);
984
            if (!$result) {
985
                static::sendErrorAlert(
986
                    'dbService Error',
987
                    'Error calling dbservice action "getUser" in ' .
988
                    'saveUserToDatastore() method.'
989
                );
990
            }
991
        } else { // Missing one or more required attributes
992
            static::setSessionVar(
993
                'status',
994
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']
995
            );
996
        }
997
998
        // If 'status' is not STATUS_OK*, then send an error email
999
        $status = static::getSessionVar('status');
1000
        if ($status & 1) { // Bad status codes are odd
1001
            // For missing parameter errors, log an error message
1002
            if ($status ==
1003
                DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) {
1004
                $log = new Loggit();
1005
                $log->error('STATUS_MISSING_PARAMETER_ERROR', true);
1006
            }
1007
1008
            // For other dbservice errors OR for any error involving
1009
            // LIGO (e.g., missing parameter error), send email alert.
1010
            if (($status !=
1011
                    DBService::$STATUS['STATUS_MISSING_PARAMETER_ERROR']) ||
1012
                (preg_match('/ligo\.org/', $databaseProviderId))) {
1013
                $mailto = '[email protected]';
1014
1015
                // Set $disableligoalerts = true to stop LIGO failures
1016
                // from being sent to '[email protected]', but still
1017
                // sent to '[email protected]'.
1018
                $disableligoalerts = false;
1019
1020
                // Fixes CIL-205 - Notify LIGO about IdP login errors
1021
                if (preg_match('/ligo\.org/', $databaseProviderId)) {
1022
                    if ($disableligoalerts) {
1023
                        $mailto = '';
1024
                    }
1025
                    $mailto .= ((strlen($mailto) > 0) ? ',' : '') .
1026
                        '[email protected]';
1027
                }
1028
1029
                static::sendErrorAlert(
1030
                    'Failure in ' .
1031
                        (($loa == 'openid') ? '' : '/secure') . '/getuser/',
1032
                    'Remote_User   = ' . ((strlen($remoteuser) > 0) ?
1033
                        $remoteuser : '<MISSING>') . "\n" .
1034
                    'IdP ID        = ' . ((strlen($databaseProviderId) > 0) ?
1035
                        $databaseProviderId : '<MISSING>') . "\n" .
1036
                    'IdP Name      = ' . ((strlen($databaseProviderName) > 0) ?
1037
                        $databaseProviderName : '<MISSING>') . "\n" .
1038
                    'First Name    = ' . ((strlen($firstname) > 0) ?
1039
                        $firstname : '<MISSING>') . "\n" .
1040
                    'Last Name     = ' . ((strlen($lastname) > 0) ?
1041
                        $lastname : '<MISSING>') . "\n" .
1042
                    'Display Name  = ' . ((strlen($displayname) > 0) ?
1043
                        $displayname : '<MISSING>') . "\n" .
1044
                    'Email Address = ' . ((strlen($emailaddr) > 0) ?
1045
                        $emailaddr : '<MISSING>') . "\n" .
1046
                    'ePPN          = ' . ((strlen($eppn) > 0) ?
1047
                        $eppn : '<MISSING>') . "\n" .
1048
                    'ePTID         = ' . ((strlen($eptid) > 0) ?
1049
                        $eptid : '<MISSING>') . "\n" .
1050
                    'OpenID ID     = ' . ((strlen($openidid) > 0) ?
1051
                        $openidid : '<MISSING>') . "\n" .
1052
                    'OIDC ID       = ' . ((strlen($oidcid) > 0) ?
1053
                        $oidcid : '<MISSING>') . "\n" .
1054
                    'Affiliation   = ' . ((strlen($affiliation) > 0) ?
1055
                        $affiliation : '<MISSING>') . "\n" .
1056
                    'OU            = ' . ((strlen($ou) > 0) ?
1057
                        $ou : '<MISSING>') . "\n" .
1058
                    'MemberOf      = ' . ((strlen($memberof) > 0) ?
1059
                        $memberof : '<MISSING>') . "\n" .
1060
                    'ACR           = ' . ((strlen($acr) > 0) ?
1061
                        $acr : '<MISSING>') . "\n" .
1062
                    'Entitlement   = ' . ((strlen($entitlement) > 0) ?
1063
                        $entitlement : '<MISSING>') . "\n" .
1064
                    'Database UID  = ' . ((strlen(
1065
                        $i = static::getSessionVar('uid')
1066
                    ) > 0) ?  $i : '<MISSING>') . "\n" .
1067
                    'Status Code   = ' . ((strlen(
1068
                        $i = array_search(
1069
                            $status,
1070
                            DBService::$STATUS
1071
                        )
1072
                    ) > 0) ?  $i : '<MISSING>'),
1073
                    $mailto
1074
                );
1075
            }
1076
            static::unsetSessionVar('authntime');
1077
        } else { // status is okay, set authntime
1078
            static::setSessionVar('authntime', time());
1079
        }
1080
1081
        static::unsetSessionVar('responsesubmit');
1082
        static::unsetSessionVar('requestsilver');
1083
1084
        static::getCsrf()->setCookieAndSession();
1085
    }
1086
1087
    /**
1088
     * unsetClientSessionVars
1089
     *
1090
     * This function removes all of the PHP session variables related to
1091
     * the client session.
1092
     */
1093
    public static function unsetClientSessionVars()
1094
    {
1095
        static::unsetSessionVar('submit');
1096
1097
        // Specific to 'Download Certificate' page
1098
        static::unsetSessionVar('activation');
1099
        static::unsetSessionVar('p12');
1100
        static::unsetSessionVar('p12lifetime');
1101
        static::unsetSessionVar('p12multiplier');
1102
1103
        // Specific to OAuth 1.0a flow
1104
        static::unsetSessionVar('portalstatus');
1105
        static::unsetSessionVar('callbackuri');
1106
        static::unsetSessionVar('successuri');
1107
        static::unsetSessionVar('failureuri');
1108
        static::unsetSessionVar('portalname');
1109
        static::unsetSessionVar('tempcred');
1110
1111
        // Specific to OIDC flow
1112
        static::unsetSessionVar('clientparams');
1113
    }
1114
1115
    /**
1116
     * unsetUserSessionVars
1117
     *
1118
     * This function removes all of the PHP session variables related to
1119
     * the user's session.  This will force the user to log on (again)
1120
     * with their IdP and call the 'getuser' script to repopulate the PHP
1121
     * session.
1122
     */
1123
    public static function unsetUserSessionVars()
1124
    {
1125
        // Needed for verifyCurrentUserSession
1126
        static::unsetSessionVar('idp');
1127
        static::unsetSessionVar('idpname');
1128
        static::unsetSessionVar('status');
1129
        static::unsetSessionVar('uid');
1130
        static::unsetSessionVar('dn');
1131
        static::unsetSessionVar('authntime');
1132
1133
        // Specific to 2FA
1134
        static::unsetSessionVar('twofactor');
1135
1136
        // Variables set by getuser
1137
        static::unsetSessionVar('firstname');
1138
        static::unsetSessionVar('lastname');
1139
        static::unsetSessionVar('displayname');
1140
        static::unsetSessionVar('emailaddr');
1141
        static::unsetSessionVar('loa');
1142
        static::unsetSessionVar('ePPN');
1143
        static::unsetSessionVar('ePTID');
1144
        static::unsetSessionVar('openidID');
1145
        static::unsetSessionVar('oidcID');
1146
        static::unsetSessionVar('affiliation');
1147
        static::unsetSessionVar('ou');
1148
        static::unsetSessionVar('memberof');
1149
        static::unsetSessionVar('acr');
1150
        static::unsetSessionVar('entitlement');
1151
1152
        // Current skin
1153
        static::unsetSessionVar('cilogon_skin');
1154
    }
1155
1156
    /**
1157
     * unsetAllUserSessionVars
1158
     *
1159
     * This is a convenience method to clear all session variables related
1160
     * to the client and the user.
1161
     */
1162
    public static function unsetAllUserSessionVars()
1163
    {
1164
        static::unsetClientSessionVars();
1165
        static::unsetUserSessionVars();
1166
    }
1167
1168
    /**
1169
     * verifySessionAndCall
1170
     *
1171
     * This function is a convenience method called by several cases in the
1172
     * main 'switch' call at the top of the index.php file. I noticed
1173
     * a pattern where verifyCurrentUserSession() was called to verify the
1174
     * current user session. Upon success, one or two functions were called
1175
     * to continue program, flow. Upon failure, cookies and session
1176
     * variables were cleared, and the main Logon page was printed. This
1177
     * function encapsulates that pattern. If the user's session is valid,
1178
     * the passed-in $func is called, possibly with parameters passed in as
1179
     * an array. The function returns true if the session is verified, so
1180
     * that other functions may be called upon return.
1181
     *
1182
     * @param function $func The function to call if the current session is
1183
     *        successfully verified.
1184
     * @param array $params (Optional) An array of parameters to pass to the
1185
     8        function. Defaults to empty array, meaning zero parameters.
1186
     */
1187
    public static function verifySessionAndCall($func, $params = array())
1188
    {
1189
        $retval = false;
1190
        if (Content::verifyCurrentUserSession()) { // Verify PHP session is valid
1191
            $retval = true;
1192
            call_user_func_array($func, $params);
1193
        } else {
1194
            printLogonPage(true); // Clear cookies and session vars too
1195
        }
1196
        return $retval;
1197
    }
1198
}
1199