Completed
Push — master ( b25e7b...4d2b11 )
by Patrick
03:36
created

LDAPUser::check_child_group()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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