Completed
Push — master ( f39199...ff19c2 )
by Patrick
03:03
created

LDAPAuthenticator::activatePendingUser()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
cc 4
eloc 18
c 6
b 2
f 0
nc 8
nop 1
dl 0
loc 26
rs 8.5806
1
<?php
2
/**
3
 * LDAPAuthenticator class
4
 *
5
 * This file describes the LDAPAuthenticator class
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
namespace Auth;
15
16
/** 
17
 * Sort the provided array by the keys in $orderby 
18
 *
19
 * @param array $array The array to sort
20
 * @param array $orderby An array of keys to sort the array by
21
 */
22
function sort_array(&$array, $orderby)
23
{
24
    $count = count($array);
25
    $keys  = array_keys($orderby);
26
    for($i = 0; $i < $count; $i++)
27
    {
28
        for($j = $i; $j < $count; $j++)
29
        {
30
            $data = strcasecmp($array[$i][$keys[0]][0], $array[$j][$keys[0]][0]);
31
            switch($orderby[$keys[0]])
32
            {
33
                case 1:
34
                    if($data > 0)
35
                    {
36
                        swap($array, $i, $j);
37
                    }
38
                    break;
39
                case 0:
40
                    if($data < 0)
41
                    {
42
                        swap($array, $i, $j);
43
                    }
44
                    break;
45
            }
46
        }
47
    }
48
}
49
50
/**
51
 * Swap two elements of the provided array
52
 *
53
 * @param array $array The array to swap values in
54
 * @param mixed $indexA The key of the first element to swap
55
 * @param mixed $indexB The key of the second element to swap
56
 */
57
function swap(&$array, $indexA, $indexB)
58
{
59
    $tmp = $array[$indexA];
60
    $array[$indexA] = $array[$indexB];
61
    $array[$indexB] = $tmp;
62
}
63
64
/**
65
 * An Authenticator class which uses LDAP as its backend storage mechanism
66
 */
67
class LDAPAuthenticator extends Authenticator
68
{
69
    /** The URL for the LDAP host */
70
    private $host;
71
    /** The base DN for all users in the LDAP server */
72
    public  $user_base;
73
    /** The base DN for all groups in the LDAP server */
74
    public  $group_base;
75
    /** The DN to use to bind if not binding as the user */
76
    private $bindDistinguishedName;
77
    /** The password to use to bind if not binding as the user */
78
    private $bindPassword;
79
80
    /**
81
     * Create an LDAP Authenticator
82
     *
83
     * @param array $params Parementers needed to initialize the authenticator
84
     */
85
    public function __construct($params)
86
    {
87
        parent::__construct($params);
88
        $this->host       = $this->getHostParam($params);
89
        $this->user_base  = $this->getParam($params, 'user_base');
90
        $this->group_base = $this->getParam($params, 'group_base');
91
        $this->bindDistinguishedName = $this->getParam($params, 'bind_dn', '$ldap_auth', 'read_write_pass');
92
        $this->bindPassword = $this->getParam($params, 'bind_pass', '$ldap_auth', 'read_write_user');
93
    }
94
95
    /**
96
     * Return the host string for this authenticator
97
     *
98
     * @param array $params The initial parameters of the authenticator
99
     *
100
     * @return string The host string
101
     *
102
     * @SuppressWarnings("StaticAccess")
103
     */
104
    private function getHostParam($params)
105
    {
106
        if(isset($params['host']))
107
        {
108
            return $params['host'];
109
        }
110
        $settings = \Settings::getInstance();
111
        return $settings->getLDAPSetting('host');
112
    }
113
114
    /**
115
     * Return the required paramter for this authenticator
116
     *
117
     * @param array  $params The initial parameters of the authenticator
118
     * @param string $paramName The name of the parameter in the $paramsArray
119
     * @param string $settingsLocation The location in the Settings class
120
     * @param string $settingsName The name in the Settings class
121
     *
122
     * @return mixed The paramter value
123
     *
124
     * @SuppressWarnings("StaticAccess")
125
     */
126
    private function getParam($params, $paramName, $settingsLocation = '$ldap', $settingsName = false)
127
    {
128
        if($settingsName === false)
129
        {
130
            $settingsName = $paramName;
131
        }
132
        if(isset($params[$paramName]))
133
        {
134
            return $params[$paramName];
135
        }
136
        $settings = \Settings::getInstance();
137
        return $settings->getLDAPSetting($settingsName, ($settingsLocation !== '$ldap'));
138
    }
139
140
    /**
141
     * Return an instance to the \LDAP\LDAPServer object instance
142
     *
143
     * @param boolean $bind_write Should we be able to write to the server?
0 ignored issues
show
Bug introduced by
There is no parameter named $bind_write. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
144
     *
145
     * @return \LDAP\LDAPServer|false The server instance if the binding was successful, otherwise false
146
     *
147
     * @SuppressWarnings("StaticAccess")
148
     */
149
    public function getAndBindServer($bindWrite = false)
150
    {
151
        $server = \LDAP\LDAPServer::getInstance();
152
        $server->user_base = $this->user_base;
153
        $server->group_base = $this->group_base;
154
        $server->connect($this->host);
155
        if($bindWrite === false)
156
        {
157
            $ret = $server->bind();
158
        }
159
        else
160
        {
161
            $ret = $server->bind($this->bindDistinguishedName, $this->bindPassword);
162
        }
163
        if($ret === false)
164
        {
165
            return false;
166
        }
167
        return $server;
168
    }
169
170
    /**
171
     * Log the user in provided the credetials
172
     *
173
     * @param string $username The UID or email address for the user
174
     * @param string $password The password for the user
175
     *
176
     * @return mixed False if the login failed and array otherwise
177
     */
178
    public function login($username, $password)
179
    {
180
        $server = $this->getAndBindServer();
181
        if($server === false)
182
        {
183
            return false;
184
        }
185
        $filter = new \Data\Filter("uid eq $username or mail eq $username");
186
        $user = $server->read($this->user_base, $filter);
0 ignored issues
show
Documentation introduced by
$filter is of type object<Data\Filter>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
187
        if($user === false || count($user) === 0)
188
        {
189
            return false;
190
        }
191
        $user = $user[0];
192
        $server->unbind();
193
        $ret = $server->bind($user->dn, $password);
194
        if($ret !== false)
195
        {
196
            return array('res'=>true, 'extended'=>$user); 
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('res' => true, 'extended' => $user); (array) is incompatible with the return type of the parent method Auth\Authenticator::login of type boolean.

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...
197
        }
198
        return false;
199
    }
200
201
    /**
202
     * Does this array represent a successful login?
203
     *
204
     * @param array $data The array data stored in the session after a login call
205
     *
206
     * @return boolean True if the user is logged in, false otherwise
207
     */
208
    public function isLoggedIn($data)
209
    {
210
        if(isset($data['res']))
211
        {
212
            return $data['res'];
213
        }
214
        return false;
215
    }
216
217
    /**
218
     * Obtain the currently logged in user from the session data
219
     *
220
     * @param \stdClass	$data The AuthData from the session
221
     *
222
     * @return \Auth\LDAPUser The LDAPUser represented by this data
223
     */
224
    public function getUser($data)
225
    {
226
        return new LDAPUser($data);
0 ignored issues
show
Documentation introduced by
$data is of type object<stdClass>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The return type of return new \Auth\LDAPUser($data); (Auth\LDAPUser) is incompatible with the return type of the parent method Auth\Authenticator::getUser of type Auth\Auth\User|null.

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...
227
    }
228
229
    public function getGroupByName($name)
230
    {
231
        $server = $this->getAndBindServer();
232
        if($server === false)
233
        {
234
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type of the parent method Auth\Authenticator::getGroupByName of type Auth\Auth\Group|null.

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...
235
        }
236
        return LDAPGroup::from_name($name, $server);
0 ignored issues
show
Documentation introduced by
$server is of type object<LDAP\LDAPServer>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The return type of return \Auth\LDAPGroup::from_name($name, $server); (false|Auth\LDAPGroup) is incompatible with the return type of the parent method Auth\Authenticator::getGroupByName of type Auth\Auth\Group|null.

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...
237
    }
238
239
    public function getGroupsByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
240
    {
241
        $server = $this->getAndBindServer();
242
        if($server === false)
243
        {
244
            return false;
245
        }
246
        if($filter === false)
247
        {
248
            $filter = new \Data\Filter('cn eq *');
249
        }
250
        $groups = $server->read($this->group_base, $filter);
251
        if($groups === false)
252
        {
253
            return false;
254
        }
255
        $this->processFilteringParams($groups, $select, $top, $skip, $orderby);
256
        $count = count($groups);
257
        for($i = 0; $i < $count; $i++)
258
        {
259
            $groups[$i] = new LDAPGroup($groups[$i]);
260
            if($select !== false)
261
            {
262
                $groups[$i] = json_decode(json_encode($groups[$i]), true);
263
                $groups[$i] = array_intersect_key($groups[$i], array_flip($select));
264
            }
265
        }
266
        return $groups;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $groups; (array) is incompatible with the return type of the parent method Auth\Authenticator::getGroupsByFilter of type boolean.

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...
267
    }
268
269
    public function getActiveUserCount()
270
    {
271
        $server = $this->getAndBindServer();
272
        if($server === false)
273
        {
274
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type of the parent method Auth\Authenticator::getActiveUserCount of type integer.

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...
275
        }
276
        return $server->count($this->user_base);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $server->count($this->user_base); of type false|integer adds false to the return on line 276 which is incompatible with the return type of the parent method Auth\Authenticator::getActiveUserCount of type integer. It seems like you forgot to handle an error condition.
Loading history...
277
    }
278
279
    /**
280
     * @param array           $data The array data to filter and sort
281
     * @param boolean|array   $select The fields to return
282
     * @param boolean|integer $top The number of records to return
283
     * @param boolean|integer $skip The number of records to skip
284
     * @param boolean|array   $orderby The fields to sort by
285
     */
286
    private function processFilteringParams(&$data, &$select, $top, $skip, $orderby)
287
    {
288
        if($orderby !== false)
289
        {
290
            sort_array($data, $orderby);
291
        }
292
        if($select !== false)
293
        {
294
            $select = array_flip($select);
295
        }
296
        if($skip !== false && $top !== false)
297
        {
298
            $data = array_slice($data, $skip, $top);
299
        }
300
        else if($top !== false)
301
        {
302
            $data = array_slice($data, 0, $top);
303
        }
304
        else if($skip !== false)
305
        {
306
            $data = array_slice($data, $skip);
307
        }
308
    }
309
310
    
311
    /**
312
     * @param boolean|\Data\Filter $filter The filter to user when reading users
313
     * @param boolean|array   $select The fields to return
314
     * @param boolean|integer $top The number of records to return
315
     * @param boolean|integer $skip The number of records to skip
316
     * @param boolean|array   $orderby The fields to sort by
317
     */
318
    public function getUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
319
    {
320
        $server = $this->getAndBindServer();
321
        if($server === false)
322
        {
323
            return false;
324
        }
325
        if($filter === false)
326
        {
327
            $filter = new \Data\Filter('cn eq *');
328
        }
329
        $users = $server->read($this->user_base, $filter, false, $select);
0 ignored issues
show
Bug introduced by
It seems like $filter can also be of type object<Data\Filter>; however, LDAP\LDAPServer::read() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $select defined by parameter $select on line 318 can also be of type array; however, LDAP\LDAPServer::read() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
330
        if($users === false)
331
        {
332
            return false;
333
        }
334
        $this->processFilteringParams($users, $select, $top, $skip, $orderby);
335
        $count = count($users);
336
        for($i = 0; $i < $count; $i++)
337
        {
338
            $tmp = new LDAPUser($users[$i]);
339
            if($select !== false)
340
            {
341
                $tmp = $tmp->jsonSerialize();
342
                $tmp = array_intersect_key($tmp, $select);
343
            }
344
            $users[$i] = $tmp;
345
        }
346
        return $users;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $users; (array) is incompatible with the return type of the parent method Auth\Authenticator::getUsersByFilter of type boolean.

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...
347
    }
348
349
    public function activatePendingUser($user)
350
    {
351
        $this->getAndBindServer(true);
352
        $newUser = new LDAPUser();
353
        $newUser->uid = $user->uid;
354
        $newUser->mail = $user->mail;
355
        $newUser->sn = $user->sn;
356
        $newUser->givenName = $user->givenName;
357
        $newUser->host = $user->host;
358
        $pass = $user->getPassword();
359
        if($pass !== false)
360
        {
361
            $newUser->setPass($pass);
362
        }
363
        $ret = $newUser->flushUser();
364
        if($ret)
365
        {
366
            $user->delete();
367
        }
368
        $users = $this->getUsersByFilter(new \Data\Filter('mail eq '.$user->mail));
369
        if(empty($users))
370
        {
371
            throw new \Exception('Error creating user!');
372
        }
373
        return $users[0];
374
    }
375
376
    public function getUserByResetHash($hash)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
377
    {
378
        $users = $this->getUsersByFilter(new \Data\Filter("uniqueIdentifier eq $hash"));
379
        if($users === false || !isset($users[0]))
380
        {
381
            return false;
382
        }
383
        return $users[0];
384
    }
385
}
386
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
387