Completed
Push — master ( d7eb82...e89277 )
by Patrick
03:09
created

class.AuthProvider.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * AuthProvider class
4
 *
5
 * This file describes the AuthProvider Singleton
6
 *
7
 * PHP version 5 and 7
8
 *
9
 * @author Patrick Boyd / [email protected]
10
 * @copyright Copyright (c) 2015, Austin Artistic Reconstruction
11
 * @license http://www.apache.org/licenses/ Apache 2.0 License
12
 */
13
14
/**
15
 * Allow other classes to be loaded as needed
16
 */
17
require_once('Autoload.php');
18
/**
19
 * Require the FlipsideSettings file
20
 */
21
require_once '/var/www/secure_settings/class.FlipsideSettings.php';
22
23
/**
24
 * A Singleton class to abstract access to the authentication providers.
25
 *
26
 * This class is the primary method to access user data, login, and other authenication information.
27
 */
28
class AuthProvider extends Singleton
29
{
30
    /** The authentication methods loaded by the provider */
31
    protected $methods;
32
33
    /**
34
     * Load the authentrication providers specified in the FlipsideSettings::$authProviders array
35
     */
36 View Code Duplication
    protected function __construct()
37
    {
38
        $this->methods = array();
39
        if(isset(FlipsideSettings::$authProviders))
40
        {
41
            $keys = array_keys(FlipsideSettings::$authProviders);
42
            $count = count($keys);
43
            for($i = 0; $i < $count; $i++)
44
            {
45
                $class = $keys[$i];
46
                array_push($this->methods, new $class(FlipsideSettings::$authProviders[$keys[$i]]));
47
            }
48
        }
49
    }
50
51
    /**
52
     * Get the Authenticator class instance by name
53
     *
54
     * @param string $methodName The class name of the Authenticator to get the instance for
55
     *
56
     * @return Auth\Authenticator|false The specified Authenticator class instance or false if it is not loaded
57
     */
58 View Code Duplication
    public function getAuthenticator($methodName)
59
    {
60
        $count = count($this->methods);
61
        for($i = 0; $i < $count; $i++)
62
        {
63
            if(strcasecmp(get_class($this->methods[$i]), $methodName) === 0)
64
            {
65
                return $this->methods[$i];
66
            }
67
        }
68
        return false;
69
    }
70
71
    /**
72
     * Get the Auth\User class instance for the specified login
73
     *
74
     * Unlike the AuthProvider::login() function. This function will not impact the SESSION
75
     *
76
     * @param string $username The username of the User
77
     * @param string $password The password of the User
78
     *
79
     * @return Auth\User|false The User with the specified credentials or false if the credentials are not valid
80
     */
81
    public function getUserByLogin($username, $password)
82
    {
83
        $res = false;
84
        $count = count($this->methods);
85
        for($i = 0; $i < $count; $i++)
86
        {
87
            $res = $this->methods[$i]->login($username, $password);
88
            if($res !== false)
89
            {
90
                return $this->methods[$i]->getUser($res);
91
            }
92
        }
93
        return $res;
94
    }
95
96
    /**
97
     * Use the provided credetials to log the user on
98
     *
99
     * @param string $username The username of the User
100
     * @param string $password The password of the User
101
     *
102
     * @return true|false true if the login was successful, false otherwise
103
     */
104
    public function login($username, $password)
105
    {
106
        $res = false;
107
        $count = count($this->methods);
108
        for($i = 0; $i < $count; $i++)
109
        {
110
            $res = $this->methods[$i]->login($username, $password);
111
            if($res !== false)
112
            {
113
                FlipSession::setVar('AuthMethod', get_class($this->methods[$i]));
114
                FlipSession::setVar('AuthData', $res);
115
                break;
116
            }
117
        }
118
        return $res;
119
    }
120
121
    /**
122
     * Determine if the user is still logged on from the session data
123
     *
124
     * @param stdClass $data The AuthData from the session
125
     * @param string $methodName The AuthMethod from the session
126
     *
127
     * @return true|false true if user is logged on, false otherwise
128
     */
129
    public function isLoggedIn($data, $methodName)
130
    {
131
        $auth = $this->getAuthenticator($methodName);
132
        return $auth->isLoggedIn($data);
133
    }
134
135
    /**
136
     * Obtain the currently logged in user from the session data
137
     *
138
     * @param stdClass $data The AuthData from the session
139
     * @param string $methodName The AuthMethod from the session
140
     *
141
     * @return Auth\User|false The User instance if user is logged on, false otherwise
142
     */
143
    public function getUser($data, $methodName)
144
    {
145
        $auth = $this->getAuthenticator($methodName);
146
        return $auth->getUser($data);
147
    }
148
149
    /**
150
     * Merge or set the returnValue as appropriate
151
     *
152
     * @param Auth\Group|Auth\User $returnValue The value to merge to
153
     * @param Auth\Group|Auth\User $res The value to merge from
154
     *
155
     * @return Auth\Group|false The merged returnValue
156
     */
157
    private function mergeResult(&$returnValue, $res)
158
    {
159
        if($res === false)
160
        {
161
            return;
162
        }
163
        if($returnValue === false)
164
        {
165
            $returnValue = $res;
166
            return;
167
        }
168
        $returnValue->merge($res);
169
    }
170
171
    /**
172
     * Calls the indicated function on each Authenticator and merges the result
173
     *
174
     * @param string $functionName The function to call
175
     * @param array $args The arguments for the function
176
     * @param string $checkField A field to check if it is set a certain way before calling the function
177
     * @param mixed $checkValue The value that field should be set to to not call the function
178
     *
179
     * @return Auth\Group|Auth\User|false The merged returnValue
180
     */
181 View Code Duplication
    private function callOnEach($functionName, $args, $checkField = false, $checkValue = false)
182
    {
183
        $ret = false;
184
        $count = count($this->methods);
185
        for($i = 0; $i < $count; $i++)
186
        {
187
            if($checkField)
188
            {
189
                if($this->methods[$i]->{$checkField} === $checkValue)
190
                {
191
                    continue;
192
                }
193
            }
194
            $res = call_user_func_array(array($this->methods[$i], $functionName), $args);
195
            $this->mergeResult($ret, $res);
0 ignored issues
show
It seems like $ret defined by false on line 183 can also be of type false; however, AuthProvider::mergeResult() does only seem to accept object<Auth\Group>|object<Auth\User>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
196
        }
197
        return $ret;
198
    }
199
200
    /**
201
     * Calls the indicated function on each Authenticator and add the result
202
     *
203
     * @param string $functionName The function to call
204
     * @param string $checkField A field to check if it is set a certain way before calling the function
205
     * @param mixed $checkValue The value that field should be set to to not call the function
206
     *
207
     * @return integer|false The added returnValue
208
     */
209 View Code Duplication
    private function addFromEach($functionName, $checkField = false, $checkValue = false)
210
    {
211
        $retCount = 0;
212
        $count = count($this->methods);
213
        for($i = 0; $i < $count; $i++)
214
        {
215
            if($checkField)
216
            {
217
                if($this->methods[$i]->{$checkField} === $checkValue)
218
                {
219
                    continue;
220
                }
221
            }
222
            $res = call_user_func(array($this->methods[$i], $functionName));
223
            $retCount += $res;
224
        }
225
        return $retCount;
226
    }
227
228
    /**
229
     * Get an Auth\Group by its name
230
     *
231
     * @param string $name The name of the group
232
     * @param string $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
233
     *
234
     * @return Auth\Group|false The Group instance if a group with that name exists, false otherwise
235
     */
236
    public function getGroupByName($name, $methodName = false)
237
    {
238
        if($methodName === false)
239
        {
240
            return $this->callOnEach('getGroupByName', array($name));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->callOnEach('getGr...ByName', array($name)); of type Auth\Group|Auth\User|false adds the type Auth\User to the return on line 240 which is incompatible with the return type documented by AuthProvider::getGroupByName of type Auth\Group|false.
Loading history...
241
        }
242
        $auth = $this->getAuthenticator($methodName);
243
        return $auth->getGroupByName($name);
244
    }
245
246
    /**
247
     * Get an array of Auth\User from a filtered set
248
     *
249
     * @param Data\Filter|false $filter The filter conditions or false to retreive all
250
     * @param array|false $methodName The user fields to obtain or false to obtain all
251
     * @param integer|false $top The number of users to obtain or false to obtain all
252
     * @param integer|false $skip The number of users to skip or false to skip none
253
     * @param array|false $orderby The field to sort by and the method to sort or false to not sort
254
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
255
     *
256
     * @return array|false An array of Auth\User objects or false if no users were found
257
     */
258 View Code Duplication
    public function getUsersByFilter($filter, $select=false, $top=false, $skip=false, $orderby=false, $methodName = false)
259
    {
260
        if($methodName === false)
261
        {
262
            return $this->callOnEach('getUsersByFilter', array($filter, $select, $top, $skip, $orderby), 'current');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->callOnEach... $orderby), 'current'); (Auth\Group|Auth\User|false) is incompatible with the return type documented by AuthProvider::getUsersByFilter of type array|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
263
        }
264
        $auth = $this->getAuthenticator($methodName);
265
        return $auth->getUsersByFilter($filter, $select, $top, $skip, $orderby);
266
    }
267
268
    /**
269
     * Get an array of Auth\PendingUser from a filtered set
270
     *
271
     * @param Data\Filter|false $filter The filter conditions or false to retreive all
272
     * @param array|false $methodName The user fields to obtain or false to obtain all
273
     * @param integer|false $top The number of users to obtain or false to obtain all
274
     * @param integer|false $skip The number of users to skip or false to skip none
275
     * @param array|false $orderby The field to sort by and the method to sort or false to not sort
276
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
277
     *
278
     * @return array|false An array of Auth\PendingUser objects or false if no pending users were found
279
     */
280 View Code Duplication
    public function getPendingUsersByFilter($filter, $select=false, $top=false, $skip=false, $orderby=false, $methodName = false)
281
    {
282
        if($methodName === false)
283
        {
284
            return $this->callOnEach('getPendingUsersByFilter', array($filter, $select, $top, $skip, $orderby), 'pending');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->callOnEach... $orderby), 'pending'); (Auth\Group|Auth\User|false) is incompatible with the return type documented by AuthProvider::getPendingUsersByFilter of type array|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
285
        }
286
        $auth = $this->getAuthenticator($methodName);
287
        return $auth->getPendingUsersByFilter($filter, $select, $top, $skip, $orderby);
288
    }
289
290
    /**
291
     * Get an array of Auth\Group from a filtered set
292
     *
293
     * @param Data\Filter|false $filter The filter conditions or false to retreive all
294
     * @param array|false $methodName The group fields to obtain or false to obtain all
295
     * @param integer|false $top The number of groups to obtain or false to obtain all
296
     * @param integer|false $skip The number of groups to skip or false to skip none
297
     * @param array|false $orderby The field to sort by and the method to sort or false to not sort
298
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
299
     *
300
     * @return array|false An array of Auth\Group objects or false if no pending users were found
301
     */
302 View Code Duplication
    public function getGroupsByFilter($filter, $select=false, $top=false, $skip=false, $orderby=false, $methodName = false)
303
    {
304
        if($methodName === false)
305
        {
306
            return $this->callOnEach('getGroupsByFilter', array($filter, $select, $top, $skip, $orderby), 'current');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->callOnEach... $orderby), 'current'); (Auth\Group|Auth\User|false) is incompatible with the return type documented by AuthProvider::getGroupsByFilter of type array|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
307
        }
308
        $auth = $this->getAuthenticator($methodName);
309
        return $auth->getGroupsByFilter($filter, $select, $top, $skip, $orderby);
310
    }
311
312
    /**
313
     * Get the number of currently active users on the system
314
     *
315
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
316
     *
317
     * @return integer The number of currently active users on the system
318
     */
319
    public function getActiveUserCount($methodName = false)
320
    {
321
        if($methodName === false)
322
        {
323
            return $this->addFromEach('getActiveUserCount', 'current');
324
        }
325
        $auth = $this->getAuthenticator($methodName);
326
        return $auth->getActiveUserCount();
327
    }
328
329
    /**
330
     * Get the number of currently pending users on the system
331
     *
332
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
333
     *
334
     * @return integer The number of currently pending users on the system
335
     */
336
    public function getPendingUserCount($methodName = false)
337
    {
338
        if($methodName === false)
339
        {
340
            return $this->addFromEach('getPendingUserCount', 'pending');
341
        }
342
        $auth = $this->getAuthenticator($methodName);
343
        return $auth->getPendingUserCount();
344
    }
345
346
    /**
347
     * Get the number of current groups on the system
348
     *
349
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
350
     *
351
     * @return integer The number of current groups on the system
352
     */
353
    public function getGroupCount($methodName = false)
354
    {
355
        if($methodName === false)
356
        {
357
            return $this->addFromEach('getGroupCount', 'current');
358
        }
359
        $auth = $this->getAuthenticator($methodName);
360
        return $auth->getGroupCount();
361
    }
362
363
    /**
364
     * Get the login links for all supplementary Authenitcation mechanisms
365
     *
366
     * This will return an array of links to any supplementary authentication mechanims. For example, Goodle is 
367
     * a supplementary authentication mechanism.
368
     *
369
     * @return array An array of suppmentary authentication mechanism links
370
     */
371
    public function getSupplementaryLinks()
372
    {
373
        $ret = array();
374
        $count = count($this->methods);
375 View Code Duplication
        for($i = 0; $i < $count; $i++)
376
        {
377
            if($this->methods[$i]->supplement === false) continue;
378
379
            array_push($ret, $this->methods[$i]->getSupplementLink());
380
        }
381
        return $ret;
382
    }
383
384
    /**
385
     * Impersonate the user specified
386
     *
387
     * This will replace the user in the session with the specified user. In order
388
     * to undo this operation a user must logout.
389
     *
390
     * @param array|Auth\User $userArray Data representing the user
391
     */
392
    public function impersonateUser($userArray)
393
    {
394
        if(!is_object($userArray))
395
        {
396
            $user = new $userArray['class']($userArray);
397
        }
398
        \FlipSession::setUser($user);
399
    }
400
401
    /**
402
     * Get the pending user reresented by the supplied hash
403
     *
404
     * @param string $hash The hash value representing the Penging User
405
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
406
     *
407
     * @return Auth\PendingUser|false The Auth\PendingUser instance or false if no user is matched by the provided hash
408
     */
409
    public function getTempUserByHash($hash, $methodName = false)
410
    {
411
        if($methodName === false)
412
        {
413
            $count = count($this->methods);
414
            for($i = 0; $i < $count; $i++)
415
            {
416
                if($this->methods[$i]->pending === false) continue;
417
418
                $ret = $this->methods[$i]->getTempUserByHash($hash);
419
                if($ret !== false)
420
                {
421
                    return $ret;
422
                }
423
            }
424
            return false;
425
        }
426
        $auth = $this->getAuthenticator($methodName);
427
        return $auth->getTempUserByHash($hash);
428
    }
429
430
    /**
431
     * Create a pending user
432
     *
433
     * @param array $user An array of information about the user to create
434
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
435
     *
436
     * @return true|false true if the user was successfully created. Otherwise false.
437
     */
438
    public function createPendingUser($user, $methodName = false)
439
    {
440
        if($methodName === false)
441
        {
442
            $count = count($this->methods);
443
            for($i = 0; $i < $count; $i++)
444
            {
445
                if($this->methods[$i]->pending === false) continue;
446
447
                $ret = $this->methods[$i]->createPendingUser($user);
448
                if($ret !== false)
449
                {
450
                    return true;
451
                }
452
            }
453
            return false;
454
        }
455
        $auth = $this->getAuthenticator($methodName);
456
        return $auth->createPendingUser($user);
457
    }
458
459
    /**
460
     * Convert a Auth\PendingUser into an Auth\User
461
     *
462
     * This will allow a previously pending user the ability to log on in the future as an active user. It will also
463
     * have the side effect of logging the user on now.
464
     *
465
     * @param Auth\PendingUser $user The user to turn into a current user
466
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
467
     *
468
     * @return true|false true if the user was successfully created. Otherwise false.
469
     */
470 View Code Duplication
    public function activatePendingUser($user, $methodName = false)
471
    {
472
        if($methodName === false)
473
        {
474
            $count = count($this->methods);
475
            for($i = 0; $i < $count; $i++)
476
            {
477
                if($this->methods[$i]->current === false) continue;
478
479
                $ret = $this->methods[$i]->activatePendingUser($user);
480
                if($ret !== false)
481
                {
482
                    $this->impersonateUser($ret);
483
                    return true;
484
                }
485
            }
486
            return false;
487
        }
488
        $auth = $this->getAuthenticator($methodName);
489
        return $auth->activatePendingUser($user);
490
    }
491
492
    /**
493
     * Get a current user by a password reset hash
494
     *
495
     * @param string $hash The current password reset hash for the user
496
     * @param string|false $methodName The AuthMethod if information is desired only from a particular Auth\Authenticator
497
     *
498
     * @return Auth\User|false The user if the password reset hash is valid. Otherwise false.
499
     */
500 View Code Duplication
    public function getUserByResetHash($hash, $methodName = false)
501
    {
502
        if($methodName === false)
503
        {
504
            $count = count($this->methods);
505
            for($i = 0; $i < $count; $i++)
506
            {
507
                if($this->methods[$i]->current === false) continue;
508
509
                $ret = $this->methods[$i]->getUserByResetHash($hash);
510
                if($ret !== false)
511
                {
512
                    return $ret;
513
                }
514
            }
515
            return false;
516
        }
517
        $auth = $this->getAuthenticator($methodName);
518
        if($auth === false)
519
        {
520
            return $this->getUserByResetHash($hash, false);
521
        }
522
        return $auth->getUserByResetHash($hash);
523
    }
524
525
    /**
526
     * Get the Auth\Authenticator by host name
527
     *
528
     * @param string $host The host name used by the supplemental authentication mechanism
529
     *
530
     * @return Auth\Authenticator|false The Authenticator if the host is supported by a loaded Authenticator. Otherwise false.
531
     */
532
    public function getSuplementalProviderByHost($host)
533
    {
534
        $count = count($this->methods);
535 View Code Duplication
        for($i = 0; $i < $count; $i++)
536
        {
537
            if($this->methods[$i]->supplement === false) continue;
538
539
            if($this->methods[$i]->getHostName() === $host)
540
            {
541
                return $this->methods[$i];
542
            }
543
        }
544
        return false;
545
    }
546
547
    public function deletePendingUsersByFilter($filter, $methodName=false)
548
    {
549
        $users = $this->getPendingUsersByFilter($filter, false, false, false, false, $methodName);
550
        if($users === false)
551
        {
552
            return false;
553
        }
554
        $count = count($users);
555
        for($i = 0; $i < $count; $i++)
556
        {
557
            $users[$i]->delete();
558
        }
559
        return true;
560
    }
561
}
562
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
563
?>
564