Completed
Push — master ( 8bc84d...a7da70 )
by Patrick
15:31 queued 04:30
created

LDAPServer::fixObject()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 2
dl 0
loc 21
rs 9.2728
c 0
b 0
f 0
1
<?php
2
namespace Flipside\LDAP;
3
4
/**
5
 * function ldap_escape
6
 * @author Chris Wright
7
 * @version 2.0
8
 * @param string $subject The subject string
9
 * @param bool $dn Treat subject as a DN if TRUE
0 ignored issues
show
Bug introduced by
There is no parameter named $dn. 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...
10
 * @param string|array $ignore Set of characters to leave untouched
11
 * @return string The escaped string
12
 */
13
function ldap_escape($subject, $distinguishedName = false, $ignore = NULL)
14
{
15
    // The base array of characters to escape
16
    // Flip to keys for easy use of unset()
17
    $search = array_flip($distinguishedName ? array('\\', '+', '<', '>', ';', '"', '#') : array('\\', '*', '(', ')', "\x00"));
18
19
    // Process characters to ignore
20
    if(is_array($ignore))
21
    {
22
        $ignore = array_values($ignore);
23
    }
24
    for($char = 0; isset($ignore[$char]); $char++)
25
    {
26
        unset($search[$ignore[$char]]);
27
    }
28
29
    // Flip $search back to values and build $replace array
30
    $search = array_keys($search);
31
    $replace = array();
32
    foreach($search as $char)
33
    {
34
        $replace[] = sprintf('\\%02x', ord($char));
35
    }
36
37
    // Do the main replacement
38
    $result = str_replace($search, $replace, $subject);
39
40
    // Encode leading/trailing spaces in DN values
41
    if($distinguishedName)
42
    {
43
        $result = cleanupDN($result);
44
    }
45
46
    return $result;
47
}
48
49
function cleanupDN($distinguishedName)
50
{
51
    if($distinguishedName[0] == ' ')
52
    {
53
        $distinguishedName = '\\20'.substr($distinguishedName, 1);
54
    }
55
    if($distinguishedName[strlen($distinguishedName) - 1] == ' ')
56
    {
57
        $distinguishedName = substr($distinguishedName, 0, -1).'\\20';
58
    }
59
    return $distinguishedName;
60
}
61
62
class LDAPServer extends \Flipside\Singleton
63
{
64
    protected $ldapLink;
65
    protected $connect;
66
    protected $binder;
67
    public $user_base;
68
    public $group_base;
69
70
    protected function __construct()
71
    {
72
        $this->ldapLink = null;
73
        $this->binder = null;
74
    }
75
76
    public function __destruct()
77
    {
78
    }
79
80
    public function __wakeup()
81
    {
82
        $this->ldapLink = ldap_connect($this->connect);
83
        ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3);
84
        ldap_set_option($this->ldapLink, LDAP_OPT_REFERRALS, false);
85
    }
86
87
    private function getConnectString($name, $proto = false)
88
    {
89
        if(strstr($name, ':') !== false)
90
        {
91
            return $name;
92
        }
93
        if($proto !== 'ldap')
94
        {
95
            return $proto.'://'.$name;
96
        }
97
        return $name;
98
    }
99
100
    public function connect($name, $proto = false)
101
    {
102
        $connectStr = $this->getConnectString($name, $proto);
103
        if($this->ldapLink !== null)
104
        {
105
            ldap_close($this->ldapLink);
106
        }
107
        $this->connect = $connectStr;
108
        $this->ldapLink = ldap_connect($this->connect);
109
        if($this->ldapLink === false)
110
        {
111
            $this->ldapLink = null;
112
            return false;
113
        }
114
        ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3);
115
        return true;
116
    }
117
118
    public function disconnect()
119
    {
120
        if($this->ldapLink !== null)
121
        {
122
            ldap_close($this->ldapLink);
123
            $this->ldapLink = null;
124
        }
125
        $this->connect = false;
126
    }
127
128
    /**
129
     * Bind (login0 to the LDAP Server
130
     *
131
     * @param string $commonName The common name to bind with, null to bind anonymously
132
     * @param string $password The password to bind with, null to bind anonymously
133
     */
134
    public function bind($commonName = null, $password = null)
135
    {
136
        $res = false;
137
        if($this->ldapLink === null)
138
        {
139
            throw new \Exception('Not connected');
140
        }
141
        $this->binder = $commonName;
142
        if($commonName === null || $password === null)
143
        {
144
            return @ldap_bind($this->ldapLink);
145
        }
146
        try
147
        {
148
            $this->ldapLink = ldap_connect($this->connect);
149
            ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3);
150
            $res = @ldap_bind($this->ldapLink, $commonName, $password);
151
        }
152
        catch(\Exception $ex)
153
        {
154
            $this->ldapLink = ldap_connect($this->connect);
155
            ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3);
156
            $res = @ldap_bind($this->ldapLink, $commonName, $password);
157
        }
158
        return $res;
159
    }
160
161
    public function unbind()
162
    {
163
        if($this->ldapLink === null)
164
        {
165
            return true;
166
        }
167
        return @ldap_unbind($this->ldapLink);
168
    }
169
170
    private function fixChildArray(&$array, $key, &$entity)
171
    {
172
        $count = count($array);
173 View Code Duplication
        for($i = 0; $i < $count; $i++)
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...
174
        {
175
            if(isset($array[$i]))
176
            {
177
                $entity[$key][$i] = $array[$i];
178
            }
179
        }
180
    }
181
182
    private function fixObject($object, &$delete = false)
183
    {
184
        $entity = $object;
185
        unset($entity['dn']);
186
        $keys = array_keys($entity);
187
        $count = count($keys);
188
        for($i = 0; $i < $count; $i++)
189
        {
190
            if(is_array($entity[$keys[$i]]))
191
            {
192
                $this->fixChildArray($entity[$keys[$i]], $keys[$i], $entity);
193
                //unset($entity[$keys[$i]]);
194
            }
195
            else if($delete !== false && $entity[$keys[$i]] === null)
196
            {
197
                $delete[$keys[$i]] = array();
198
                unset($entity[$keys[$i]]);
199
            }
200
        }
201
        return $entity;
202
    }
203
204
    public function create($object)
205
    {
206
        $distinguishedName = ldap_escape($object['dn'], true);
207
        $entity = $this->fixObject($object);
208
        $ret = ldap_add($this->ldapLink, $distinguishedName, $entity);
209
        if($ret === false)
210
        {
211
            throw new \Exception('Failed to create object with dn='.$distinguishedName);
212
        }
213
        return $ret;
214
    }
215
216
    /**
217
     * Get the LDAP filter represented by the passed object
218
     *
219
     * @param boolean|string|\Data\Filter $filter The fiter to use
220
     *
221
     * @return string The filter in LDAP format
222
     */
223
    private function filterToString($filter)
224
    {
225
        if($filter === false)
226
        {
227
            return '(objectclass=*)';
228
        }
229
        if(is_string($filter))
230
        {
231
            return $filter;
232
        }
233
        return $filter->to_ldap_string();
234
    }
235
236
    private function searchResultToArray($searchResult)
237
    {
238
        if($searchResult === false)
239
        {
240
            return false;
241
        }
242
        $res = ldap_get_entries($this->ldapLink, $searchResult);
243
        if(is_array($res))
244
        {
245
            $ldap = $res;
246
            $res = array();
247
            for($i = 0; $i < $ldap['count']; $i++)
248
            {
249
                array_push($res, new LDAPObject($ldap[$i], $this));
0 ignored issues
show
Documentation introduced by
$this is of type this<Flipside\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...
250
            }
251
        }
252
        return $res;
253
    }
254
255
    /**
256
     * Get data from the LDAP Server
257
     *
258
     * @param string $baseDN The distinguished name to start the search from
259
     * @param boolean|string|\Data\Filter $filter The fiter to use
260
     * @param boolean $single Read only the base DN
261
     * @param boolean|array $attributes The list of attributes to read
262
     *
263
     * @return boolean|array The results from the LDAP Server
264
     */
265
    public function read($baseDN, $filter = false, $single = false, $attributes = false)
266
    {
267
        $filterStr = $this->filterToString($filter);
268
        if($this->ldapLink === null)
269
        {
270
            throw new \Exception('Not connected');
271
        }
272
        try
273
        {
274
            if($single === true)
275
            {
276
                $searchResult = @ldap_read($this->ldapLink, $baseDN, $filterStr);
277
                return $this->searchResultToArray($searchResult);
278
            }
279
            if($attributes !== false)
280
            {
281
                $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr, $attributes);
282
                return $this->searchResultToArray($searchResult);
283
            }
284
            $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr);
285
            return $this->searchResultToArray($searchResult);
286
        }
287
        catch(\Exception $e)
288
        {
289
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
290
        }
291
    }
292
293
    public function count($baseDN, $filter = false)
294
    {
295
        $filterStr = $this->filterToString($filter);
296
        if($this->ldapLink === null)
297
        {
298
            throw new \Exception('Not connected');
299
        }
300
        try
301
        {
302
            $searchResult = ldap_list($this->ldapLink, $baseDN, $filterStr, array('dn'));
303
        }
304
        catch(\Exception $e)
305
        {
306
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
307
        }
308
        if($searchResult === false)
309
        {
310
            return 0;
311
        }
312
        return ldap_count_entries($this->ldapLink, $searchResult);
313
    }
314
315
    public function update($object)
316
    {
317
        $distinguishedName = ldap_escape($object['dn'], true);
318
        $delete = array();
319
        $entity = $this->fixObject($object, $delete);
0 ignored issues
show
Documentation introduced by
$delete is of type array, 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...
320
        $ret = false;
321
        if(!empty($entity))
322
        {
323
            $ret = @ldap_mod_replace($this->ldapLink, $distinguishedName, $entity);
324
            if($ret === false)
325
            {
326
                $string = 'Failed to update object with dn='.$distinguishedName.' ('.ldap_errno($this->ldapLink).':'.ldap_error($this->ldapLink).') '.print_r($entity, true);
327
                throw new \Exception($string);
328
            }
329
        }
330
        if(!empty($delete))
331
        {
332
            $ret = @ldap_mod_del($this->ldapLink, $distinguishedName, $delete);
333
        }
334
        return $ret;
335
    }
336
337
    public function delete($distinguishedName)
338
    {
339
        return ldap_delete($this->ldapLink, $distinguishedName);
340
    }
341
}
342
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
343