Ldaph::searchHelper()   B
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
ccs 0
cts 8
cp 0
rs 8.8571
cc 5
eloc 8
nc 8
nop 2
crap 30
1
<?php namespace Comodojo\Ldaph;
2
3
use \Comodojo\Exception\LdaphException;
4
use \Exception;
5
6
/**
7
 * ldaph: poor man's php ldap class
8
 *
9
 * @package     Comodojo Spare Parts
10
 * @author      Marco Giovinazzi <[email protected]>
11
 * @license     MIT
12
 *
13
 * LICENSE:
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
 * THE SOFTWARE.
22
 */
23
24
class Ldaph {
25
26
    /**
27
     * Ldap handler
28
     *
29
     * @var resource
30
     */
31
    private $ldaph = false;
32
33
    /**
34
     * Ldap version
35
     *
36
     * @var int
37
     */
38
    private $version = 3;
39
40
    /**
41
     * ldaps support
42
     *
43
     * @var bool
44
     */
45
    private $ssl = false;
46
47
    /**
48
     * Use tls
49
     *
50
     * @var bool
51
     */
52
    private $tls = false;
53
54
    /**
55
     * SSO support
56
     *
57
     * @var bool
58
     */
59
    private $sso = false;
60
61
    /**
62
     * DC(s)
63
     *
64
     * @var string
65
     */
66
    private $dc = '';
67
68
    /**
69
     * DN
70
     *
71
     * @var string
72
     */
73
    private $dn = 'USER_NAME';
74
75
    /**
76
     * The base to search in
77
     *
78
     * @var string
79
     */
80
    private $searchbase = null;
81
82
    /**
83
     * Ldap username
84
     *
85
     * @var string
86
     */
87
    private $user = null;
88
89
    /**
90
     * Ldap user password
91
     *
92
     * @var string
93
     */
94
    private $pass = null;
95
96
    /**
97
     * Ldap server port
98
     *
99
     * @var int
100
     */
101
    private $port = 389;
102
103
    /**
104
     * Ldap server
105
     *
106
     * @var string
107
     */
108
    private $server = null;
109
110
    /**
111
     * Fields to return
112
     *
113
     * @var array
114
     */
115
    private $fields = array();
116
117
    /**
118
     * Constructor method
119
     *
120
     * Prepare environment for connection (bind)
121
     *
122
     * @param   string  $server ldap server (ip or FQDN)
123
     * @param   int     $port   port to connect to
124
     *
125
     * @throws  LdaphException
126
     */
127 15
    public function __construct($server, $port = 389) {
128
129 15
        if ( empty($server) ) throw new LdaphException("Invalid LDAP parameters", 1401);
130
131 12
        if ( !function_exists("ldap_connect") ) throw new LdaphException("PHP ldap extension not available", 1407);
132
133 12
        $this->server = $server;
134
135 12
        $this->port = filter_var($port, FILTER_VALIDATE_INT, array(
136 4
            "options" => array(
137 8
                "min_range" => 1,
138 8
                "max_range" => 65535,
139
                "default" => 389
140 8
                )
141 8
            )
142 8
        );
143
144 12
    }
145
146
    /**
147
     * Set ldap base
148
     *
149
     * @param   string  $dcs    ldap base, comma separated, not spaced
150
     *
151
     * @return  Ldaph
152
     *
153
     * @throws  LdaphException
154
     */
155 9 View Code Duplication
    final public function base($dcs) {
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...
156
157 9
        if ( empty($dcs) ) throw new LdaphException($dcs, 1410);
158
159 9
        $pDc = preg_replace('/\s*,\s*/', ',', $dcs);
160
161 9
        $this->dc = $pDc;
162
163 9
        return $this;
164
165
    }
166
167
    /**
168
     * Set ldap distinguished name (used in ldap bind)
169
     *
170
     * Before bind, special word USERNAME will be substituted by real username
171
     *
172
     * @param   string  $dn    ldap DN, comma separated, not spaced
173
     *
174
     * @return  Ldaph
175
     *
176
     * @throws  LdaphException
177
     */
178 12
    final public function dn($dn) {
179
180 12
        if ( empty($dn) ) throw new LdaphException($dn, 1411);
181
182 12
        $this->dn = preg_replace('/\s*,\s*/', ',', $dn);
183
184 12
        return $this;
185
186
    }
187
188
    /**
189
     * Set ldap version: 2 or 3 (default)
190
     *
191
     * @param   int $mode   ldap protocol version
192
     *
193
     * @return  Ldaph
194
     */
195 12
    final public function version($mode = 3) {
196
197 12
        $mode = filter_var($mode, FILTER_VALIDATE_INT);
198
199 12
        if ( $mode === 2 ) {
200
            $this->version = 2;
201
        } else {
202 12
            $this->version = 3;
203
        }
204
205 12
        return $this;
206
207
    }
208
209
    /**
210
     * Enable/disable ssl for connection
211
     *
212
     * @param   bool    $mode
213
     *
214
     * @return  Ldaph
215
     */
216 12 View Code Duplication
    final public function ssl($mode = true) {
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...
217
218 12
        $mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
219
220 12
        if ( $mode === true ) {
221
            $this->ssl = true;
222
        } else {
223 12
            $this->ssl = false;
224
        }
225
226 12
        return $this;
227
228
    }
229
230
    /**
231
     * Enable/disable tls for connection
232
     *
233
     * @param   bool    $mode
234
     *
235
     * @return  Ldaph
236
     */
237 12 View Code Duplication
    final public function tls($mode = true) {
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...
238
239 12
        $mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
240
241 12
        if ( $mode === true ) {
242
            $this->tls = true;
243
        } else {
244 12
            $this->tls = false;
245
        }
246
247 12
        return $this;
248
249
    }
250
251
    /**
252
     * Enable/disable single sign on
253
     *
254
     * @param   bool    $mode
255
     *
256
     * @return  Ldaph
257
     *
258
     * @throws  LdaphException
259
     */
260 3
    final public function sso($mode = true) {
261
262 3
        $mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
263
264 3
        if ( $mode === true ) {
265
266
            if ( !function_exists('ldap_sasl_bind') ) throw new LdaphException("No LDAP SSO support", 1408);
267
268
            $this->sso = true;
269
270
        } else {
271
272 3
            $this->sso = false;
273
274
        }
275
276 3
        return $this;
277
278
    }
279
280
    /**
281
     * Set user/pass for bind and search
282
     *
283
     * @param   string  $user
284
     * @param   string  $pass
285
     *
286
     * @return  Ldaph
287
     *
288
     * @throws  LdaphException
289
     */
290 9 View Code Duplication
    final public function account($user, $pass) {
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...
291
292 9
        if ( empty($user) OR empty($pass) ) throw new LdaphException("Invalid LDAP user/pass", 1402);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
293
294 9
        $this->user = $user;
295 9
        $this->pass = $pass;
296
297 9
        return $this;
298
299
    }
300
301
    /**
302
     * Set ldap search base
303
     *
304
     * During search, special word PATTERN will be sbstituted by provided pattern
305
     *
306
     * @param   string  $s
307
     *
308
     * @return  Ldaph
309
     */
310 9
    final public function searchbase($s) {
311
312 9
        if ( empty($s) ) {
313
            $this->searchbase = null;
314
        } else {
315 9
            $this->searchbase = preg_replace('/\s*,\s*/', ',', $s);
316
        }
317
318 9
        return $this;
319
320
    }
321
322
    /**
323
     * Set fields to query ldap for
324
     *
325
     * @param   mixed    $f
326
     *
327
     * @return  Ldaph
328
     */
329 6
    final public function fields($f) {
330
331 6
        if ( empty($f) ) $this->fields = array();
332
333 6
        elseif ( is_array($f) ) $this->fields = $f;
334
335
        else $this->fields = array($f);
336
337 6
        return $this;
338
339
    }
340
341
    /**
342
     * Authenticate an user via LDAP
343
     *
344
     * @param   string  $userName   The user to auth
345
     * @param   string  $userPass   The password for user
346
     *
347
     * @return  bool
348
     *
349
     * @throws  LdaphException
350
     * @throws  Exception
351
     */
352 3
    public function auth($userName, $userPass) {
353
354 3
        if ( empty($userName) OR empty($userPass) ) throw new LdaphException("Invalid LDAP user/pass", 1402);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
355
356
        try {
357
358 3
            $auth = $this->setupConnection($userName, $userPass);
359
360 3
        } catch (LdaphException $le) {
361
362 3
            $this->unsetConnection();
363
364 3
            throw $le;
365
366
        } catch (Exception $e) {
367
368
            $this->unsetConnection();
369
370
            throw $e;
371
372
        }
373
374
        $this->unsetConnection();
375
376
        return $auth;
377
378
    }
379
380
    /**
381
     * Search ldap directory for $what
382
     *
383
     * @param   string  $what   The pattern to search for (will replace the searcbase PATTERN special word)
384
     * @param   bool    $clean  If true, raw ldap_get_entries result will be normalized as plain array
385
     *
386
     * @return  array
387
     *
388
     * @throws  LdaphException
389
     * @throws  Exception
390
     */
391 6
    public function search($what = "*", $clean = false) {
392
393
        try {
394
395 6
            $this->setupConnection($this->user, $this->pass);
396
397
            ldap_set_option($this->ldaph, LDAP_OPT_SIZELIMIT, 0);
398
399
            $result = $this->searchHelper($what, filter_var($clean, FILTER_VALIDATE_BOOLEAN));
400
401 6
        } catch (LdaphException $le) {
402
403 6
            $this->unsetConnection();
404
405 6
            throw $le;
406
407
        } catch (Exception $e) {
408
409
            $this->unsetConnection();
410
411
            throw $e;
412
413
        }
414
415
        $this->unsetConnection();
416
417
        return $result;
418
419
    }
420
421
    /**
422
     * Setup LDAP connection
423
     *
424
     * @param string $user
425
     * @param string $pass
426
     *
427
     * @return  bool
428
     *
429
     * @throws  LdaphException
430
     */
431 9
    private function setupConnection($user = null, $pass = null) {
0 ignored issues
show
Coding Style introduced by
setupConnection uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
432
433 9
        $this->ldaph = $this->ssl ? ldap_connect("ldaps://".$this->server, $this->port) : ldap_connect($this->server, $this->port);
434
435 9
        if ( !$this->ldaph ) throw new LdaphException(ldap_error($this->ldaph), 1403);
436
437 9
        ldap_set_option($this->ldaph, LDAP_OPT_PROTOCOL_VERSION, $this->version);
438 9
        ldap_set_option($this->ldaph, LDAP_OPT_REFERRALS, 0);
439
440 9
        if ( $this->tls ) {
441
442
            $tls = @ldap_start_tls($this->ldaph);
443
444
            if ( $tls === false ) throw new LdaphException(ldap_error($this->ldaph), 1403);
445
446
        }
447
448 9
        if ( $this->sso AND $_SERVER['REMOTE_USER'] AND $_SERVER["REMOTE_USER"] == $user AND $_SERVER["KRB5CCNAME"] ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
449
450
            putenv("KRB5CCNAME=".$_SERVER["KRB5CCNAME"]);
451
452
            $bind = @ldap_sasl_bind($this->ldaph, null, null, "GSSAPI");
453
454 9
        } elseif ( is_null($user) OR is_null($pass) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
455
456
            $bind = @ldap_bind($this->ldaph);
457
458
        } else {
459
460 9
            $user_dn = str_replace('USERNAME', $user, $this->dn);
461 9
            $bind = @ldap_bind($this->ldaph, $user_dn, $pass);
462
463
        }
464
465 9
        if ( !$bind ) throw new LdaphException(ldap_error($this->ldaph), 1402);
466
467
        return true;
468
469
    }
470
471
    /**
472
     * Unset a previously opened ldap connection
473
     */
474 9
    private function unsetConnection() {
475
476 9
        @ldap_unbind($this->ldaph);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
477
478 9
    }
479
480
    /**
481
     * Helper for $this->search()
482
     *
483
     * @param string $what
484
     * @param bool   $clean
485
     *
486
     * @return  array
487
     *
488
     * @throws  LdaphException
489
     */
490
    private function searchHelper($what, $clean) {
491
492
        $base = $this->dc;
493
494
        $search = str_replace('PATTERN', $what, $this->searchbase);
495
496
        $result = empty($this->fields) ? ldap_search($this->ldaph, $base, $search) : ldap_search($this->ldaph, $base, $search, $this->fields);
497
498
        if ( !$result ) throw new LdaphException(ldap_error($this->ldaph), 1404);
499
500
        $to_return = ldap_get_entries($this->ldaph, $result);
501
502
        if ( $to_return === false ) throw new LdaphException(ldap_error($this->ldaph), 1412);
503
504
        return $clean ? $this->searchCleaner($to_return) : $to_return;
505
506
    }
507
508
    /**
509
     * Normalize ldap search result into plain array
510
     *
511
     * @return array
512
     */
513
    private function searchCleaner($results) {
514
515
        $entry = array();
516
517
        unset($results['count']);
518
519
        foreach ( $results as $key => $result ) {
520
521
            unset($result["count"]);
522
523
            $valid = true;
524
525
            foreach ( $this->fields as $field ) {
526
527
                if ( !array_key_exists(strtolower($field), $result) ) $valid = false;
528
529
            }
530
531
            if ( !$valid ) {
532
533
                unset($result[$key]);
534
                continue;
535
536
            } else {
537
538
                $entry[$key] = array();
539
540
            }
541
542
            foreach ( $result as $subkey => $value ) {
543
544
                if ( is_int($subkey) OR $subkey == "count" ) continue;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
545
546
                else {
547
548
                    if ( is_scalar($value) ) {
549
                        $entry[$key][$subkey] = $value;
550
                    }
551
552
                    if ( is_array($value) ) {
553
554
                        if ( $value["count"] == 1 ) {
555
                            $entry[$key][$subkey] = $value[0];
556
                        } else {
557
                            unset($value["count"]);
558
                            $entry[$key][$subkey] = $value;
559
                        }
560
561
                    }
562
563
                }
564
565
            }
566
567
        }
568
569
        return $entry;
570
571
    }
572
573
}
574