Passed
Pull Request — master (#32)
by Tim
03:02
created

Ldap::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 2
eloc 18
c 2
b 1
f 0
nc 2
nop 6
dl 0
loc 34
rs 9.6666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\ldap\Connector;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\Error;
9
use SimpleSAML\Logger;
10
use SimpleSAML\Module\ldap\ConnectorInterface;
11
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
12
use Symfony\Component\Ldap\Entry;
13
use Symfony\Component\Ldap\Exception\InvalidCredentialsException;
14
use Symfony\Component\Ldap\Ldap as LdapObject;
15
16
use function array_merge;
17
use function array_pop;
18
use function explode;
19
use function implode;
20
use function ini_get;
21
use function sprintf;
22
use function var_export;
23
24
class Ldap implements ConnectorInterface
25
{
26
    use LdapHelpers;
27
28
    /**
29
     * @var \Symfony\Component\Ldap\Adapter\ExtLdap\Adapter
30
     */
31
    protected Adapter $adapter;
32
33
    /**
34
     * @var \Symfony\Component\Ldap\Ldap
35
     */
36
    protected LdapObject $connection;
37
38
39
    /**
40
     * @param string $connection_strings
41
     * @param string $encryption
42
     * @param int $version
43
     * @param string $extension
44
     * @param bool $debug
45
     * @param array $options
46
     */
47
    public function __construct(
48
        string $connection_strings,
49
        string $encryption = 'ssl',
50
        int    $version = 3,
51
        string $extension = 'ext_ldap',
0 ignored issues
show
Unused Code introduced by
The parameter $extension is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

51
        /** @scrutinizer ignore-unused */ string $extension = 'ext_ldap',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
52
        bool   $debug = false,
53
        array  $options = ['referrals' => false, 'network_timeout' => 3]
54
    ) {
55
        foreach (explode(' ', $connection_strings) as $connection_string) {
56
            Assert::regex($connection_string, '#^ldap[s]?:\/\/#');
57
        }
58
59
        Logger::debug(
60
            sprintf(
61
                "Setting up LDAP connection: host='%s', encryption=%s, version=%d, debug=%s, timeout=%d, referrals=%s.",
62
                $connection_strings,
63
                $encryption,
64
                $version,
65
                var_export($debug, true),
66
                $options['timeout'] ?? ini_get('default_socket_timeout'),
67
                var_export($options['referrals'] ?? false, true),
68
            ));
69
70
        $this->adapter = new Adapter(
71
            [
72
                'connection_string' => $connection_strings,
73
                'encryption'        => $encryption,
74
                'version'           => $version,
75
                'debug'             => $debug,
76
                'options'           => $options,
77
            ]
78
        );
79
80
        $this->connection = new LdapObject($this->adapter);
81
    }
82
83
84
    /**
85
     * @inheritDoc
86
     */
87
    public function bind(string $username, ?string $password): void
88
    {
89
        try {
90
            $this->connection->bind($username, strval($password));
91
        } catch (InvalidCredentialsException $e) {
92
            throw new Error\Error($this->resolveBindError($e));
93
        }
94
95
        Logger::debug(sprintf("LDAP bind(): Bind successful for DN '%s'.", $username));
96
    }
97
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function search(
103
        array  $searchBase,
104
        string $filter,
105
        array  $options,
106
        bool   $allowMissing
107
    ): ?Entry
108
    {
109
        $entry = null;
110
111
        foreach ($searchBase as $base) {
112
            $query  = $this->connection->query($base, $filter, $options);
113
            $result = $query->execute()->toArray();
114
115
            if (count($result) > 1) {
116
                throw new Error\Exception(
117
                    sprintf(
118
                        "LDAP search(): Found %d entries searching base '%s' for '%s'",
119
                        count($result),
120
                        $base,
121
                        $filter,
122
                    )
123
                );
124
            } elseif (count($result) === 1) {
125
                $entry = array_pop($result);
0 ignored issues
show
Bug introduced by
$result of type Symfony\Component\Ldap\Adapter\list is incompatible with the type array expected by parameter $array of array_pop(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
                $entry = array_pop(/** @scrutinizer ignore-type */ $result);
Loading history...
126
                break;
127
            } else {
128
                Logger::debug(
129
                    sprintf(
130
                        "LDAP search(): Found no entries searching base '%s' for '%s'",
131
                        $base,
132
                        $filter,
133
                    )
134
                );
135
            }
136
        }
137
138
        if ($entry === null && $allowMissing === false) {
139
            throw new Error\Exception(
140
                sprintf(
141
                    "Object not found using search base [%s] and filter '%s'",
142
                    implode(', ', $searchBase),
143
                    $filter
144
                )
145
            );
146
        }
147
148
        return $entry;
149
    }
150
151
152
    /**
153
     * @inheritDoc
154
     */
155
    public function searchForMultiple(
156
        array  $searchBase,
157
        string $filter,
158
        array  $options,
159
        bool   $allowMissing
160
    ): array
161
    {
162
        $results = [];
163
164
        foreach ($searchBase as $base) {
165
            $query   = $this->connection->query($base, $filter, $options);
166
            $result  = $query->execute()->toArray();
167
            $results = array_merge($results, $result);
0 ignored issues
show
Bug introduced by
$result of type Symfony\Component\Ldap\Adapter\list is incompatible with the type array expected by parameter $arrays of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

167
            $results = array_merge($results, /** @scrutinizer ignore-type */ $result);
Loading history...
168
169
            Logger::debug(sprintf(
170
                "Library - LDAP search(): Found %d entries searching base '%s' for '%s'",
171
                count($result),
172
                $base,
173
                $filter,
174
            ));
175
        }
176
177
        if (empty($results) && ($allowMissing === false)) {
178
            throw new Error\Exception(
179
                sprintf(
180
                    "No Objects found using search base [%s] and filter '%s'",
181
                    implode(', ', $searchBase),
182
                    $filter
183
                )
184
            );
185
        }
186
187
        return $results;
188
    }
189
190
191
    /**
192
     * Resolve the message to a UI exception
193
     *
194
     * @param InvalidCredentialsException $e
195
     * @return string
196
     */
197
    protected function resolveBindError(InvalidCredentialsException $e): string
0 ignored issues
show
Unused Code introduced by
The parameter $e is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

197
    protected function resolveBindError(/** @scrutinizer ignore-unused */ InvalidCredentialsException $e): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
198
    {
199
        return self::ERR_WRONG_PASS;
200
    }
201
}
202