Completed
Push — master ( f02d55...68b6aa )
by Patrick
03:40
created

LDAPUser::validate_reset_hash()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 9.4285
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
        $this->initialize($data);
15
    }
16
17
    private function check_child_group($array)
18
    {
19
        $res = false;
20
        for($i = 0; $i < $array['count']; $i++)
21
        {
22
            if(strpos($array[$i], $this->server->group_base) !== false)
23
            {
24
                $dn = explode(',', $array[$i]);
25
                $res = $this->isInGroupNamed(substr($dn[0], 3));
26
                if($res)
27
                {
28
                    return $res;
29
                }
30
            }
31
        }
32
        return $res;
33
    }
34
35
    /**
36
     * @param string $listName The name of the list to search
37
     * @param Group $group The group to search inside
38
     * @param string $dn The distringuished name to search for
39
     */
40
    private function isInListOrChild($listName, $group, $dn)
41
    {
42
        if(!isset($group[$listName]))
43
        {
44
            return false;
45
        }
46
        if(in_array($dn, $group[$listName]))
47
        {
48
            return true;
49
        }
50
        return $this->check_child_group($group[$listName]);
51
    }
52
53
    public function isInGroupNamed($name)
54
    {
55
        $filter = new \Data\Filter('cn eq '.$name);
56
        $group = $this->server->read($this->server->group_base, $filter);
57
        if(!empty($group))
58
        {
59
            $group = $group[0];
60
            $dn  = $this->ldapObj->dn;
61
            $uid = $this->ldapObj->uid[0];
62
            $ret = $this->isInListOrChild('member', $group, $dn);
63
            if($ret === false)
64
            {
65
                $ret = $this->isInListOrChild('uniquemember', $group, $dn);
66
            }
67
            if($ret === false && isset($group['memberUid']) && in_array($uid, $group['memberUid']))
68
            {
69
                return true;
70
            }
71
            return $ret;
72
        }
73
        return false;
74
    }
75
76
    protected $valueDefaults = array(
77
        'o' => 'Volunteer'
78
    );
79
80
    protected $multiValueProps = array(
81
        'title',
82
        'ou',
83
        'host'
84
    );
85
86
    protected $cachedOnlyProps = array(
87
        'uid'
88
    );
89
90
    protected function getValueWithDefault($propName)
91
    {
92
        if(isset($this->valueDefaults[$propName]))
93
        {
94
            $tmp = $this->getFieldSingleValue($propName);
95
            if($tmp === false)
96
            {
97
                return $this->valueDefaults[$propName];
98
            }
99
            return $tmp;
100
        }
101
        return false;
102
    }
103
104
    protected function getMultiValueProp($propName)
105
    {
106
        if(in_array($propName, $this->multiValueProps))
107
        {
108
            $tmp = $this->getField($propName);
109
            if(isset($tmp['count']))
110
            {
111
                unset($tmp['count']);
112
            }
113
            return $tmp;
114
        }
115
        return false;
116
    }
117
118
    public function __get($propName)
119
    {
120
        $tmp = $this->getValueWithDefault($propName);
121
        if($tmp !== false)
122
        {
123
            return $tmp;
124
        }
125
        $tmp = $this->getMultiValueProp($propName);
126
        if($tmp !== false)
127
        {
128
            return $tmp;
129
        }
130
        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...
131
    }
132
133
    protected function setCachedOnlyProp($propName, $value)
134
    {
135
        if(in_array($propName, $this->cachedOnlyProps))
136
        {
137
            if(!is_object($this->ldapObj))
138
            {
139
                $this->setFieldLocal($propName, $value);
140
                return true;
141
            }
142
            throw new \Exception('Unsupported!');
143
        }
144
        return false;
145
    }
146
147
    protected function setMultiValueProp($propName, $value)
148
    {
149
        if(in_array($propName, $this->multiValueProps) && !is_array($value))
150
        {
151
             $this->setField($propName, array($value));
152
             return true;
153
        }
154
        return false;
155
    }
156
157
    public function __set($propName, $value)
158
    {
159
        if($this->setCachedOnlyProp($propName, $value) === true)
160
        {
161
            return;
162
        }
163
        if($this->setMultiValueProp($propName, $value) === true)
164
        {
165
            return;
166
        }
167
        $this->setField($propName, $value);
168
    }
169
170
    public function getGroups()
171
    {
172
        $res = array();
173
        $groups = $this->server->read($this->server->group_base);
174
        if(!empty($groups))
175
        {
176
            $count = count($groups);
177
            for($i = 0; $i < $count; $i++)
178
            {
179
                if($this->isInGroupNamed($groups[$i]['cn'][0]))
180
                {
181
                    array_push($res, new LDAPGroup($groups[$i]));
182
                }
183
            }
184
            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...
185
        }
186
        else
187
        {
188
            return false;
189
        }
190
    }
191
192
    public function addLoginProvider($provider)
193
    {
194
        return $this->appendField('host', $provider);
195
    }
196
197
    private function generateLDAPPass($pass)
198
    {
199
        mt_srand((double)microtime() * 1000000);
200
        $salt = pack("CCCC", mt_rand(), mt_rand(), mt_rand(), mt_rand());
201
        $hash = base64_encode(pack('H*', sha1($pass.$salt)).$salt);
202
        return '{SSHA}'.$hash;
203
    }
204
205
    public function setPass($password)
206
    {
207
        if(!is_object($this->ldapObj))
208
        {
209
            return $this->setFieldLocal('userPassword', $this->generateLDAPPass($password));
210
        }
211
        else
212
        {
213
            $obj = array('dn'=>$this->ldapObj->dn);
214
            $obj['userPassword'] = $this->generateLDAPPass($password);
215
            if(isset($this->ldapObj->uniqueidentifier))
216
            {
217
                $obj['uniqueIdentifier'] = null;
218
            }
219
            //Make sure we are bound in write mode
220
            $auth = \AuthProvider::getInstance();
221
            $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
222
            $ldap->get_and_bind_server(true);
223
            return $this->update($obj);
224
        }
225
    }
226
227
    public function validate_password($password)
228
    {
229
        if($this->server->bind($this->ldapObj->dn, $password))
230
        {
231
            return true;
232
        }
233
        return false;
234
    }
235
236
    public function validate_reset_hash($hash)
237
    {
238
        if(isset($this->ldapObj->uniqueidentifier) && strcmp($this->ldapObj->uniqueidentifier[0], $hash) === 0)
239
        {
240
            return true;
241
        }
242
        return false;
243
    }
244
245 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...
246
    {
247
        if($data === false)
248
        {
249
            throw new \Exception('data must be set for LDAPUser');
250
        }
251
        $filter = new \Data\Filter("uid eq $name");
252
        $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...
253
        if($user === false || !isset($user[0]))
254
        {
255
            return false;
256
        }
257
        return new static($user[0]);
258
    }
259
260 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...
261
    {
262
        if($data === false)
263
        {
264
            throw new \Exception('data must be set for LDAPUser');
265
        }
266
        $filter = new \Data\Filter("dn eq $dn");
267
        $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...
268
        if($user === false || !isset($user[0]))
269
        {
270
            return false;
271
        }
272
        return new static($user[0]);
273
    }
274
275
    public function flushUser()
276
    {
277
        if(is_object($this->ldapObj))
278
        {
279
            //In this mode we are always up to date
280
            return true;
281
        }
282
        $obj = $this->ldapObj;
283
        $obj['objectClass'] = array('top', 'inetOrgPerson', 'extensibleObject');
284
        $obj['dn'] = 'uid='.$this->ldapObj['uid'].','.$this->server->user_base;
285
        if(!isset($obj['sn']))
286
        {
287
            $obj['sn'] = $obj['uid'];
288
        }
289
        if(!isset($obj['cn']))
290
        {
291
            $obj['cn'] = $obj['uid'];
292
        }
293
        $ret = $this->server->create($obj);
294
        return $ret;
295
    }
296
297
    public function getPasswordResetHash()
298
    {
299
        //Make sure we are bound in write mode
300
        $auth = \AuthProvider::getInstance();
301
        $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
302
        $ldap->get_and_bind_server(true);
303
        $ldapObj = $this->server->read($ldap->user_base, new \Data\Filter('uid eq '.$this->uid));
304
        $ldapObj = $ldapObj[0];
305
        $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...
306
        if(isset($ldapObj->userpassword))
307
        {
308
            $hash = hash('sha512', $ldapObj->dn.';'.$ldapObj->userpassword[0].';'.$ldapObj->mail[0]);
309
        }
310
        else
311
        {
312
            $hash = hash('sha512', $ldapObj->dn.';'.openssl_random_pseudo_bytes(10).';'.$ldapObj->mail[0]);
313
        }
314
        $obj = array('dn'=>$this->ldapObj->dn);
315
        $obj['uniqueIdentifier'] = $hash;
316
        if($this->server->update($obj) === false)
317
        {
318
            throw new \Exception('Unable to create hash in LDAP object!');
319
        }
320
        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...
321
    }
322
323
    public function delete()
324
    {
325
        //Make sure we are bound in write mode
326
        $auth = \AuthProvider::getInstance();
327
        $ldap = $auth->getMethodByName('Auth\LDAPAuthenticator');
328
        $ldap->get_and_bind_server(true);
329
        return $this->server->delete($this->ldapObj->dn);
330
    }
331
}
332
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
333