Completed
Push — develop ( ed6c74...ceecfe )
by Patrick
04:06
created

LDAP/LDAPServer.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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++)
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
        if(!is_array($object))
186
        {
187
            $entity = $object->to_array();
188
        }
189
        unset($entity['dn']);
190
        $keys = array_keys($entity);
191
        $count = count($keys);
192
        for($i = 0; $i < $count; $i++)
193
        {
194
            if(is_array($entity[$keys[$i]]))
195
            {
196
                $this->fixChildArray($entity[$keys[$i]], $keys[$i], $entity);
197
                //unset($entity[$keys[$i]]);
198
            }
199
            else if($delete !== false && $entity[$keys[$i]] === null)
200
            {
201
                $delete[$keys[$i]] = array();
202
                unset($entity[$keys[$i]]);
203
            }
204
        }
205
        return $entity;
206
    }
207
208
    public function create($object)
209
    {
210
        $distinguishedName = ldap_escape($object['dn'], true);
211
        $entity = $this->fixObject($object);
212
        $ret = ldap_add($this->ldapLink, $distinguishedName, $entity);
213
        if($ret === false)
214
        {
215
            throw new \Exception('Failed to create object with dn='.$distinguishedName);
216
        }
217
        return $ret;
218
    }
219
220
    /**
221
     * Get the LDAP filter represented by the passed object
222
     *
223
     * @param boolean|string|\Data\Filter $filter The fiter to use
224
     *
225
     * @return string The filter in LDAP format
226
     */
227
    private function filterToString($filter)
228
    {
229
        if($filter === false)
230
        {
231
            return '(objectclass=*)';
232
        }
233
        if(is_string($filter))
234
        {
235
            return $filter;
236
        }
237
        return $filter->to_ldap_string();
238
    }
239
240
    private function searchResultToArray($searchResult)
241
    {
242
        if($searchResult === false)
243
        {
244
            return false;
245
        }
246
        $res = ldap_get_entries($this->ldapLink, $searchResult);
247
        if(is_array($res))
248
        {
249
            $ldap = $res;
250
            $res = array();
251
            for($i = 0; $i < $ldap['count']; $i++)
252
            {
253
                array_push($res, new LDAPObject($ldap[$i], $this));
0 ignored issues
show
$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...
254
            }
255
        }
256
        return $res;
257
    }
258
259
    /**
260
     * Get data from the LDAP Server
261
     *
262
     * @param string $baseDN The distinguished name to start the search from
263
     * @param boolean|string|\Data\Filter $filter The fiter to use
264
     * @param boolean $single Read only the base DN
265
     * @param boolean|array $attributes The list of attributes to read
266
     *
267
     * @return boolean|array The results from the LDAP Server
268
     */
269
    public function read($baseDN, $filter = false, $single = false, $attributes = false)
270
    {
271
        $filterStr = $this->filterToString($filter);
272
        if($this->ldapLink === null)
273
        {
274
            throw new \Exception('Not connected');
275
        }
276
        try
277
        {
278
            if($single === true)
279
            {
280
                $searchResult = @ldap_read($this->ldapLink, $baseDN, $filterStr);
281
                return $this->searchResultToArray($searchResult);
282
            }
283
            if($attributes !== false)
284
            {
285
                $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr, $attributes);
286
                return $this->searchResultToArray($searchResult);
287
            }
288
            $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr);
289
            return $this->searchResultToArray($searchResult);
290
        }
291
        catch(\Exception $e)
292
        {
293
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
294
        }
295
    }
296
297
    public function count($baseDN, $filter = false)
298
    {
299
        $filterStr = $this->filterToString($filter);
300
        if($this->ldapLink === null)
301
        {
302
            throw new \Exception('Not connected');
303
        }
304
        try
305
        {
306
            $searchResult = ldap_list($this->ldapLink, $baseDN, $filterStr, array('dn'));
307
        }
308
        catch(\Exception $e)
309
        {
310
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
311
        }
312
        if($searchResult === false)
313
        {
314
            return 0;
315
        }
316
        return ldap_count_entries($this->ldapLink, $searchResult);
317
    }
318
319
    public function update($object)
320
    {
321
        $distinguishedName = ldap_escape($object['dn'], true);
322
        $delete = array();
323
        $entity = $this->fixObject($object, $delete);
324
        $ret = false;
325
        if(!empty($entity))
326
        {
327
            $ret = @ldap_mod_replace($this->ldapLink, $distinguishedName, $entity);
328
            if($ret === false)
329
            {
330
                $string = 'Failed to update object with dn='.$distinguishedName.' ('.ldap_errno($this->ldapLink).':'.ldap_error($this->ldapLink).') '.print_r($entity, true);
331
                throw new \Exception($string);
332
            }
333
        }
334
        if(!empty($delete))
335
        {
336
            $ret = @ldap_mod_del($this->ldapLink, $distinguishedName, $delete);
337
        }
338
        return $ret;
339
    }
340
341
    public function delete($distinguishedName)
342
    {
343
        return ldap_delete($this->ldapLink, $distinguishedName);
344
    }
345
}
346
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
347