Completed
Branch MagicUser (0f8166)
by Patrick
03:40
created

LDAPUser::__get()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 22
rs 8.6737
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 View Code Duplication
        else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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)
48
                {
49
                    return $res;
50
                }
51
            }
52
        }
53
        return $res;
54
    }
55
56
    /**
57
     * @param string $listName The name of the list to search
58
     * @param Group $group The group to search inside
59
     * @param string $dn The distringuished name to search for
60
     */
61
    private function isInListOrChild($listName, $group, $dn)
62
    {
63
        if(!isset($group[$listName]))
64
        {
65
            return false;
66
        }
67
        if(in_array($dn, $group[$listName]))
68
        {
69
            return true;
70
        }
71
        return $this->check_child_group($group[$listName]);
72
    }
73
74
    public function isInGroupNamed($name)
75
    {
76
        $filter = new \Data\Filter('cn eq '.$name);
77
        $group = $this->server->read($this->server->group_base, $filter);
78
        if(!empty($group))
79
        {
80
            $group = $group[0];
81
            $dn  = $this->ldapObj->dn;
82
            $uid = $this->ldapObj->uid[0];
83
            $ret = $this->isInListOrChild('member', $group, $dn);
84
            if($ret === false)
85
            {
86
                $ret = $this->isInListOrChild('uniquemember', $group, $dn);
87
            }
88
            if($ret === false && isset($group['memberUid']) && in_array($uid, $group['memberUid']))
89
            {
90
                return true;
91
            }
92
            return $ret;
93
        }
94
        return false;
95
    }
96
97
    protected $valueDefaults = array(
98
        'o' => 'Volunteer'
99
    );
100
101
    protected $multiValueProps = array(
102
        'title',
103
        'ou',
104
        'host'
105
    );
106
107
    public function __get($propName)
108
    {
109
        if(isset($this->valueDefaults[$propName]))
110
        {
111
            $tmp = $this->getFieldSingleValue($propName);
112
            if($tmp === false)
113
            {
114
                return $this->valueDefaults[$propName];
115
            }
116
            return $tmp;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $tmp; (string) is incompatible with the return type of the parent method Auth\User::__get 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...
117
        }
118
        if(in_array($propName, $this->multiValueProps))
119
        {
120
            $tmp = $this->getField($propName);
121
            if(isset($tmp['count']))
122
            {
123
                unset($tmp['count']);
124
            }
125
            return $tmp;
126
        }
127
        return $this->getFieldSingleValue($propName);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFieldSingleValue($propName); (string) is incompatible with the return type of the parent method Auth\User::__get 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...
128
    }
129
130
    public function __set($propName, $value)
131
    {
132
    }
133
134
    public function getGroups()
135
    {
136
        $res = array();
137
        $groups = $this->server->read($this->server->group_base);
138
        if(!empty($groups))
139
        {
140
            $count = count($groups);
141
            for($i = 0; $i < $count; $i++)
142
            {
143
                if($this->isInGroupNamed($groups[$i]['cn'][0]))
144
                {
145
                    array_push($res, new LDAPGroup($groups[$i]));
146
                }
147
            }
148
            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...
149
        }
150
        else
151
        {
152
            return false;
153
        }
154
    }
155
156
    public function addLoginProvider($provider)
157
    {
158
        return $this->appendField('host', $provider);
159
    }
160
161
    private function generateLDAPPass($pass)
162
    {
163
        mt_srand((double)microtime() * 1000000);
164
        $salt = pack("CCCC", mt_rand(), mt_rand(), mt_rand(), mt_rand());
165
        $hash = base64_encode(pack('H*', sha1($pass.$salt)).$salt);
166
        return '{SSHA}'.$hash;
167
    }
168
169
    public function setPass($password)
170
    {
171
        if(!is_object($this->ldapObj))
172
        {
173
            return $this->setFieldLocal('userPassword', $this->generateLDAPPass($password));
174
        }
175
        else
176
        {
177
            $obj = array('dn'=>$this->ldapObj->dn);
178
            $obj['userPassword'] = $this->generateLDAPPass($password);
179
            if(isset($this->ldapObj->uniqueidentifier))
180
            {
181
                $obj['uniqueIdentifier'] = null;
182
            }
183
            //Make sure we are bound in write mode
184
            $auth = \AuthProvider::getInstance();
185
            $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
186
            $ldap->get_and_bind_server(true);
187
            return $this->update($obj);
188
        }
189
    }
190
191
    public function validate_password($password)
192
    {
193
        if($this->server->bind($this->ldapObj->dn, $password))
194
        {
195
            return true;
196
        }
197
        return false;
198
    }
199
200
    public function validate_reset_hash($hash)
201
    {
202
        if(isset($this->ldapObj->uniqueidentifier) && strcmp($this->ldapObj->uniqueidentifier[0], $hash) === 0)
203
        {
204
            return true;
205
        }
206
        return false;
207
    }
208
209 View Code Duplication
    public static function from_name($name, $data = false)
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...
210
    {
211
        if($data === false)
212
        {
213
            throw new \Exception('data must be set for LDAPUser');
214
        }
215
        $filter = new \Data\Filter("uid eq $name");
216
        $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...
217
        if($user === false || !isset($user[0]))
218
        {
219
            return false;
220
        }
221
        return new static($user[0]);
222
    }
223
224 View Code Duplication
    public static function from_dn($dn, $data = false)
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...
225
    {
226
        if($data === false)
227
        {
228
            throw new \Exception('data must be set for LDAPUser');
229
        }
230
        $filter = new \Data\Filter("dn eq $dn");
231
        $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...
232
        if($user === false || !isset($user[0]))
233
        {
234
            return false;
235
        }
236
        return new static($user[0]);
237
    }
238
239
    public function setDisplayName($name)
240
    {
241
        return $this->setField('displayName', $name);
242
    }
243
244
    public function setGivenName($name)
245
    {
246
        return $this->setField('givenName', $name);
247
    }
248
249
    public function setLastName($sn)
250
    {
251
        return $this->setField('sn', $sn);
252
    }
253
254
    public function setEmail($email)
255
    {
256
        return $this->setField('mail', $email);
257
    }
258
259
    public function setUid($uid)
260
    {
261
        if(!is_object($this->ldapObj))
262
        {
263
            return $this->setFieldLocal('uid', $uid);
264
        }
265
        else
266
        {
267
            throw new \Exception('Unsupported!');
268
        }
269
    }
270
271
    public function setPhoto($photo)
272
    {
273
        return $this->setField('jpegPhoto', $photo);
274
    }
275
276
    public function setAddress($address)
277
    {
278
        return $this->setField('postalAddress', $address);
279
    }
280
281
    public function setPostalCode($postalcode)
282
    {
283
        $postalcode = trim($postalcode);
284
        return $this->setField('postalCode', $postalcode);
285
    }
286
287
    public function setCountry($country)
288
    {
289
        return $this->setField('c', $country);
290
    }
291
292
    public function setState($state)
293
    {
294
        return $this->setField('st', $state);
295
    }
296
297
    public function setCity($city)
298
    {
299
        return $this->setField('l', $city);
300
    }
301
302
    public function setPhoneNumber($phone)
303
    {
304
        return $this->setField('mobile', $phone);
305
    }
306
307
    public function setTitles($titles)
308
    {
309
        if(!is_array($titles))
310
        {
311
            $titles = array($titles);
312
        }
313
        return $this->setField('title', $titles);
314
    }
315
316
    public function setOrganizationUnits($ous)
317
    {
318
        if(!is_array($ous))
319
        {
320
            $ous = array($ous);
321
        }
322
        return $this->setField('ou', $ous);
323
    }
324
325
    public function flushUser()
326
    {
327
        if(is_object($this->ldapObj))
328
        {
329
            //In this mode we are always up to date
330
            return true;
331
        }
332
        $obj = $this->ldapObj;
333
        $obj['objectClass'] = array('top', 'inetOrgPerson', 'extensibleObject');
334
        $obj['dn'] = 'uid='.$this->ldapObj['uid'].','.$this->server->user_base;
335
        if(!isset($obj['sn']))
336
        {
337
            $obj['sn'] = $obj['uid'];
338
        }
339
        if(!isset($obj['cn']))
340
        {
341
            $obj['cn'] = $obj['uid'];
342
        }
343
        $ret = $this->server->create($obj);
344
        return $ret;
345
    }
346
347
    public function getPasswordResetHash()
348
    {
349
        //Make sure we are bound in write mode
350
        $auth = \AuthProvider::getInstance();
351
        $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
352
        $ldap->get_and_bind_server(true);
353
        $ldapObj = $this->server->read($ldap->user_base, new \Data\Filter('uid eq '.$this->uid));
0 ignored issues
show
Documentation introduced by
The property uid does not exist on object<Auth\LDAPUser>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
354
        $ldapObj = $ldapObj[0];
355
        $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...
356
        if(isset($ldapObj->userpassword))
357
        {
358
            $hash = hash('sha512', $ldapObj->dn.';'.$ldapObj->userpassword[0].';'.$ldapObj->mail[0]);
359
        }
360
        else
361
        {
362
            $hash = hash('sha512', $ldapObj->dn.';'.openssl_random_pseudo_bytes(10).';'.$ldapObj->mail[0]);
363
        }
364
        $obj = array('dn'=>$this->ldapObj->dn);
365
        $obj['uniqueIdentifier'] = $hash;
366
        if($this->server->update($obj) === false)
367
        {
368
            throw new \Exception('Unable to create hash in LDAP object!');
369
        }
370
        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...
371
    }
372
373
    public function delete()
374
    {
375
        //Make sure we are bound in write mode
376
        $auth = \AuthProvider::getInstance();
377
        $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
378
        $ldap->get_and_bind_server(true);
379
        return $this->server->delete($this->ldapObj->dn);
380
    }
381
}
382
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
383