Completed
Push — master ( 448bfe...be88f9 )
by Patrick
03:11
created

LDAPUser::getTitles()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 9
rs 9.6666
c 1
b 1
f 0
1
<?php
2
namespace Auth;
3
4
class LDAPUser extends User
5
{
6
    use LDAPCachableObject;
7
8
    private $ldapObj;
9
    private $server;
10
11
    public function __construct($data=false)
12
    {
13
        $this->server = \LDAP\LDAPServer::getInstance();
14
        if($data !== false && !isset($data['dn']) && !isset($data['extended']))
15
        {
16
            //Generic user object
17
            $filter = new \Data\Filter('mail eq '.$data['mail']);
18
            $users = $this->server->read($this->server->user_base, $filter);
19
            if($users === false || !isset($users[0]))
20
            {
21
                throw new \Exception('No such LDAP User!');
22
            }
23
            $this->ldapObj = $users[0];
24
        }
25
        else
26
        {
27
            if(isset($data['extended']))
28
            {
29
                $this->ldapObj = $data['extended'];
30
            }
31
            else
32
            {
33
                $this->ldapObj = $data;
34
            }
35
        }
36
    }
37
38
    private function check_child_group($array)
39
    {
40
        $res = false;
41
        for($i = 0; $i < $array['count']; $i++)
42
        {
43
            if(strpos($array[$i], $this->server->group_base) !== false)
44
            {
45
                $dn = explode(',', $array[$i]);
46
                $res = $this->isInGroupNamed(substr($dn[0], 3));
47
                if($res) return $res;
48
            }
49
        }
50
        return $res;
51
    }
52
53
    private function isInListOrChild($listName, $group, $dn)
54
    {
55
        if(!isset($group[$listName]))
56
        {
57
            return false;
58
        }
59
        if(in_array($dn, $group[$listName]))
60
        {
61
            return true;
62
        }
63
        return $this->check_child_group($group[$listName]);
64
    }
65
66
    public function isInGroupNamed($name)
67
    {
68
        $filter = new \Data\Filter('cn eq '.$name);
69
        $group = $this->server->read($this->server->group_base, $filter);
70
        if(!empty($group))
71
        {
72
            $group = $group[0];
73
            $dn  = $this->ldapObj->dn;
74
            $uid = $this->ldapObj->uid[0];
75
            $ret = $this->isInListOrChild('member', $group, $dn);
76
            if($ret === false)
77
            {
78
                $ret = $this->isInListOrChild('uniquemember', $group, $dn);
79
            }
80
            if($ret === false && isset($group['memberUid']) && in_array($uid, $group['memberUid']))
81
            {
82
                return true;
83
            }
84
            return $ret;
85
        }
86
        return false;
87
    }
88
89
    public function getDisplayName()
90
    {
91
        return $this->getFieldSingleValue('displayName');
92
    }
93
94
    public function getGivenName()
95
    {
96
        return $this->getFieldSingleValue('givenName');
97
    }
98
99
    public function getEmail()
100
    {
101
        return $this->getFieldSingleValue('mail');
102
    }
103
104
    public function getUid()
105
    {
106
        return $this->getFieldSingleValue('uid');
107
    }
108
109
    public function getPhoto()
110
    {
111
        return $this->getFieldSingleValue('jpegPhoto');
112
    }
113
114
    public function getPhoneNumber()
115
    {
116
        return $this->getFieldSingleValue('mobile');
117
    }
118
119
    public function getOrganization()
120
    {
121
        $org = $this->getFieldSingleValue('o');
122
        if($org === false)
123
        {
124
            return 'Volunteer';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'Volunteer'; (string) is incompatible with the return type of the parent method Auth\User::getOrganization 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...
125
        }
126
        return $org;
127
    }
128
129
    public function getTitles()
130
    {
131
        $titles = $this->getField('title');
132
        if(isset($titles['count']))
133
        {
134
            unset($titles['count']);
135
        }
136
        return $titles;
137
    }
138
139
    public function getState()
140
    {
141
        return $this->getFieldSingleValue('st');
142
    }
143
144
    public function getCity()
145
    {
146
        return $this->getFieldSingleValue('l');
147
    }
148
149
    public function getLastName()
150
    {
151
        return $this->getFieldSingleValue('sn');
152
    }
153
154
    public function getNickName()
155
    {
156
        return $this->getFieldSingleValue('cn');
157
    }
158
159
    public function getAddress()
160
    {
161
        return $this->getFieldSingleValue('postalAddress');
162
    }
163
164
    public function getPostalCode()
165
    {
166
        return $this->getFieldSingleValue('postalCode');
167
    }
168
169
    public function getCountry()
170
    {
171
        return $this->getFieldSingleValue('c');
172
    }
173
174
    public function getOrganizationUnits()
175
    {
176
        $units = $this->getField('ou');
177
        if(isset($units['count']))
178
        {
179
            unset($units['count']);
180
        }
181
        return $units;
182
    }
183
184
    public function getLoginProviders()
185
    {
186
        $hosts = $this->getField('host');
187
        if(isset($hosts['count']))
188
        {
189
            unset($hosts['count']);
190
        }
191
        return $hosts;
192
    }
193
194
    public function getGroups()
195
    {
196
        $res = array();
197
        $groups = $this->server->read($this->server->group_base);
198
        if(!empty($groups))
199
        {
200
            $count = count($groups);
201
            for($i = 0; $i < $count; $i++)
202
            {
203
                if($this->isInGroupNamed($groups[$i]['cn'][0]))
204
                {
205
                    array_push($res, new LDAPGroup($groups[$i]));
206
                }
207
            }
208
            return $res;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $res; (array) is incompatible with the return type of the parent method Auth\User::getGroups 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...
209
        }
210
        else
211
        {
212
            return false;
213
        }
214
    }
215
216
    public function addLoginProvider($provider)
217
    {
218
        return $this->appendField('host', $provider);
219
    }
220
221
    private function generateLDAPPass($pass)
222
    {
223
        mt_srand((double)microtime()*1000000);
224
        $salt = pack("CCCC", mt_rand(), mt_rand(), mt_rand(), mt_rand());
225
        $hash = base64_encode(pack('H*',sha1($pass.$salt)).$salt);
226
        return '{SSHA}'.$hash;
227
    }
228
229
    public function setPass($password)
230
    {
231
        if(!is_object($this->ldapObj))
232
        {
233
            return $this->setFieldLocal('userPassword',  $this->generateLDAPPass($password));
234
        }
235
        else
236
        {
237
            $obj = array('dn'=>$this->ldapObj->dn);
238
            $obj['userPassword'] = $this->generateLDAPPass($password);
239
            if(isset($this->ldapObj->uniqueidentifier))
240
            {
241
               $obj['uniqueIdentifier'] = null;
242
            }
243
            //Make sure we are bound in write mode
244
            $auth = \AuthProvider::getInstance();
245
            $ldap = $auth->getAuthenticator('Auth\LDAPAuthenticator');
246
            $ldap->get_and_bind_server(true);
247
            return $this->update($obj);
248
        }
249
    }
250
251
    public function validate_password($password)
252
    {
253
        if($this->server->bind($this->ldapObj->dn, $password))
254
        {
255
            return true;
256
        }
257
        return false;
258
    }
259
260
    public function validate_reset_hash($hash)
261
    {
262
        if(isset($this->ldapObj->uniqueidentifier) && strcmp($this->ldapObj->uniqueidentifier[0], $hash) === 0)
263
        {
264
            return true;
265
        }
266
        return false;
267
    }
268
269 View Code Duplication
    static function from_name($name, $data=false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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...
270
    {
271
        if($data === false)
272
        {
273
            throw new \Exception('data must be set for LDAPUser');
274
        }
275
        $filter = new \Data\Filter("uid eq $name");
276
        $user = $data->read($data->user_base, $filter);
0 ignored issues
show
Bug introduced by
The method read cannot be called on $data (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
277
        if($user === false || !isset($user[0]))
278
        {
279
            return false;
280
        }
281
        return new static($user[0]);
282
    }
283
284 View Code Duplication
    static function from_dn($dn, $data=false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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...
285
    {
286
        if($data === false)
287
        {
288
            throw new \Exception('data must be set for LDAPUser');
289
        }
290
        $filter = new \Data\Filter("dn eq $dn");
291
        $user = $data->read($data->user_base, $filter);
0 ignored issues
show
Bug introduced by
The method read cannot be called on $data (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
292
        if($user === false || !isset($user[0]))
293
        {
294
            return false;
295
        }
296
        return new static($user[0]);
297
    }
298
299
    public function setDisplayName($name)
300
    {
301
        return $this->setField('displayName', $name);
302
    }
303
304
    public function setGivenName($name)
305
    {
306
        return $this->setField('givenName', $name);
307
    }
308
309
    public function setLastName($sn)
310
    {
311
        return $this->setField('sn', $sn);
312
    }
313
314
    public function setEmail($email)
315
    {
316
        return $this->setField('mail', $email);
317
    }
318
319
    public function setUid($uid)
320
    {
321
        if(!is_object($this->ldapObj))
322
        {
323
            return $this->setFieldLocal('uid', $uid);
324
        }
325
        else
326
        {
327
            throw new \Exception('Unsupported!');
328
        }
329
    }
330
331
    public function setPhoto($photo)
332
    {
333
        return $this->setField('jpegPhoto', $photo);
334
    }
335
336
    public function setAddress($address)
337
    {
338
        return $this->setField('postalAddress', $address);
339
    }
340
341
    public function setPostalCode($postalcode)
342
    {
343
        $postalcode = trim($postalcode);
344
        return $this->setField('postalCode', $postalcode);
345
    }
346
347
    public function setCountry($country)
348
    {
349
        return $this->setField('c', $country);
350
    }
351
352
    public function setState($state)
353
    {
354
        return $this->setField('st', $state);
355
    }
356
357
    public function setCity($city)
358
    {
359
        return $this->setField('l', $city);
360
    }
361
362
    public function setPhoneNumber($phone)
363
    {
364
        return $this->setField('mobile', $phone);
365
    }
366
367
    public function setTitles($titles)
368
    {
369
        if(!is_array($titles))
370
        {
371
            $titles = array($titles);
372
        }
373
        return $this->setField('title', $titles);
374
    }
375
376
    public function setOrganizationUnits($ous)
377
    {
378
        if(!is_array($ous))
379
        {
380
            $ous = array($ous);
381
        }
382
        return $this->setField('ou', $ous);
383
    }
384
385
    public function flushUser()
386
    {
387
        if(is_object($this->ldapObj))
388
        {
389
            //In this mode we are always up to date
390
            return true;
391
        }
392
        $obj = $this->ldapObj;
393
        $obj['objectClass'] = array('top', 'inetOrgPerson', 'extensibleObject');
394
        $obj['dn'] = 'uid='.$this->ldapObj['uid'].','.$this->server->user_base;
395
        if(!isset($obj['sn']))
396
        {
397
            $obj['sn'] = $obj['uid'];
398
        }
399
        if(!isset($obj['cn']))
400
        {
401
            $obj['cn'] = $obj['uid'];
402
        }
403
        $ret = $this->server->create($obj);
404
        return $ret;
405
    }
406
407
    public function getPasswordResetHash()
408
    {
409
        //Make sure we are bound in write mode
410
        $auth = \AuthProvider::getInstance();
411
        $ldap = $auth->getAuthenticator('Auth\LDAPAuthenticator');
412
        $ldap->get_and_bind_server(true);
413
        $ldapObj = $this->server->read($ldap->user_base, new \Data\Filter('uid eq '.$this->getUid()));
414
        $ldapObj = $ldapObj[0];
415
        $hash = false;
0 ignored issues
show
Unused Code introduced by
$hash 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...
416
        if(isset($ldapObj->userpassword))
417
        {
418
            $hash = hash('sha512', $ldapObj->dn.';'.$ldapObj->userpassword[0].';'.$ldapObj->mail[0]);
419
        }
420
        else
421
        {
422
            $hash = hash('sha512', $ldapObj->dn.';'.openssl_random_pseudo_bytes(10).';'.$ldapObj->mail[0]);
423
        }
424
        $obj = array('dn'=>$this->ldapObj->dn);
425
        $obj['uniqueIdentifier'] = $hash;
426
        if($this->server->update($obj) === false)
427
        {
428
            throw new \Exception('Unable to create hash in LDAP object!');
429
        }
430
        return $hash;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $hash; (string) is incompatible with the return type of the parent method Auth\User::getPasswordResetHash 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...
431
    }
432
433
    public function delete()
434
    {
435
        //Make sure we are bound in write mode
436
        $auth = \AuthProvider::getInstance();
437
        $ldap = $auth->getAuthenticator('Auth\LDAPAuthenticator');
438
        $ldap->get_and_bind_server(true);
439
        return $this->server->delete($this->ldapObj->dn);
440
    }
441
}
442
443
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
444