Passed
Push — master ( dcac97...8c737f )
by Tim
06:57
created

Ldap::resolveBindException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
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\AdapterInterface;
12
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
13
use Symfony\Component\Ldap\Entry;
14
use Symfony\Component\Ldap\Exception\InvalidCredentialsException;
15
use Symfony\Component\Ldap\Exception\LdapException;
16
use Symfony\Component\Ldap\Ldap as LdapObject;
17
18
use function array_merge;
19
use function array_pop;
20
use function explode;
21
use function implode;
22
use function ini_get;
23
use function sprintf;
24
use function var_export;
25
26
class Ldap implements ConnectorInterface
27
{
28
    use LdapHelpers;
29
30
    /**
31
     * @var \Symfony\Component\Ldap\Adapter\AdapterInterface
32
     */
33
    protected AdapterInterface $adapter;
34
35
    /**
36
     * @var \Symfony\Component\Ldap\Ldap
37
     */
38
    protected LdapObject $connection;
39
40
41
    /**
42
     * @param string $connection_strings
43
     * @param string $encryption
44
     * @param int $version
45
     * @param string $extension
46
     * @param bool $debug
47
     * @param array<mixed> $options
48
     */
49
    public function __construct(
50
        string $connection_strings,
51
        string $encryption = 'ssl',
52
        int $version = 3,
53
        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

53
        /** @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...
54
        bool $debug = false,
55
        array $options = ['referrals' => false, 'network_timeout' => 3],
56
    ) {
57
        foreach (explode(' ', $connection_strings) as $connection_string) {
58
            Assert::regex($connection_string, '#^ldap[s]?:\/\/#');
59
        }
60
61
        Logger::debug(sprintf(
62
            "Setting up LDAP connection: host='%s', encryption=%s, version=%d, debug=%s, timeout=%d, referrals=%s.",
63
            $connection_strings,
64
            $encryption,
65
            $version,
66
            var_export($debug, true),
67
            $options['timeout'] ?? ini_get('default_socket_timeout'),
68
            var_export($options['referrals'] ?? false, true),
69
        ));
70
71
        $this->adapter = new Adapter(
72
            [
73
                'connection_string' => $connection_strings,
74
                'encryption'        => $encryption,
75
                'version'           => $version,
76
                'debug'             => $debug,
77
                'options'           => $options,
78
            ],
79
        );
80
81
        $this->connection = new LdapObject($this->adapter);
82
    }
83
84
85
    /**
86
     * @return \Symfony\Component\Ldap\Adapter\AdapterInterface
87
     */
88
    public function getAdapter(): AdapterInterface
89
    {
90
        return $this->adapter;
91
    }
92
93
94
    /**
95
     * @inheritDoc
96
     */
97
    public function bind(?string $username, #[\SensitiveParameter]?string $password): void
98
    {
99
        try {
100
            $this->connection->bind($username, strval($password));
101
        } catch (InvalidCredentialsException $e) {
102
            throw new Error\Error($this->resolveBindException($e));
103
        }
104
105
        if ($username === null) {
106
            Logger::debug("LDAP bind(): Anonymous bind succesful.");
107
        } else {
108
            Logger::debug(sprintf("LDAP bind(): Bind successful for DN '%s'.", $username));
109
        }
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115
    public function saslBind(
116
        ?string $username,
117
        #[\SensitiveParameter]?string $password,
118
        ?string $mech,
119
        ?string $realm,
120
        ?string $authcId,
121
        ?string $authzId,
122
        ?string $props,
123
    ): void {
124
        if (!method_exists($this->connection, 'saslBind')) {
125
            throw new Error\Error("SASL not implemented");
126
        }
127
128
        try {
129
            $this->connection->saslBind($username, strval($password), $mech, $realm, $authcId, $authzId, $props);
130
        } catch (InvalidCredentialsException $e) {
131
            throw new Error\Error($this->resolveBindException($e));
132
        }
133
134
        if ($username === null) {
135
            Logger::debug("LDAP bind(): Anonymous bind succesful.");
136
        } else {
137
            Logger::debug(sprintf("LDAP bind(): Bind successful for DN '%s'.", $username));
138
        }
139
    }
140
141
    /**
142
     * @inheritDoc
143
     */
144
    public function whoami(): string
145
    {
146
        if (!method_exists($this->connection, 'whoami')) {
147
            throw new Error\Error("SASL not implemented");
148
        }
149
150
        return $this->connection->whoami();
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156
    public function search(
157
        array $searchBase,
158
        string $filter,
159
        array $options,
160
        bool $allowMissing,
161
    ): ?Entry {
162
        $entry = null;
163
164
        foreach ($searchBase as $base) {
165
            $query  = $this->connection->query($base, $filter, $options);
166
            $result = $query->execute()->toArray();
167
168
            if (count($result) > 1) {
169
                throw new Error\Exception(sprintf(
170
                    "LDAP search(): Found %d entries searching base '%s' for '%s'",
171
                    count($result),
172
                    $base,
173
                    $filter,
174
                ));
175
            } elseif (count($result) === 1) {
176
                $entry = array_pop($result);
177
                break;
178
            } else {
179
                Logger::debug(sprintf(
180
                    "LDAP search(): Found no entries searching base '%s' for '%s'",
181
                    $base,
182
                    $filter,
183
                ));
184
            }
185
        }
186
187
        if ($entry === null && $allowMissing === false) {
188
            throw new Error\Exception(sprintf(
189
                "Object not found using search base [%s] and filter '%s'",
190
                implode(', ', $searchBase),
191
                $filter,
192
            ));
193
        }
194
195
        return $entry;
196
    }
197
198
199
    /**
200
     * @inheritDoc
201
     */
202
    public function searchForMultiple(
203
        array $searchBase,
204
        string $filter,
205
        array $options,
206
        bool $allowMissing,
207
    ): array {
208
        $results = [];
209
210
        foreach ($searchBase as $base) {
211
            $query   = $this->connection->query($base, $filter, $options);
212
            $result  = $query->execute()->toArray();
213
            $results = array_merge($results, $result);
214
215
            Logger::debug(sprintf(
216
                "Library - LDAP search(): Found %d entries searching base '%s' for '%s'",
217
                count($result),
218
                $base,
219
                $filter,
220
            ));
221
        }
222
223
        if (empty($results) && ($allowMissing === false)) {
224
            throw new Error\Exception(sprintf(
225
                "No Objects found using search base [%s] and filter '%s'",
226
                implode(', ', $searchBase),
227
                $filter,
228
            ));
229
        }
230
231
        return $results;
232
    }
233
234
235
    /**
236
     * Resolve the message to a UI exception
237
     *
238
     * @param InvalidCredentialsException $e
239
     * @return string
240
     */
241
    protected function resolveBindException(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

241
    protected function resolveBindException(/** @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...
242
    {
243
        return self::ERR_WRONG_PASS;
244
    }
245
246
247
    /**
248
     * @param \Symfony\Component\Ldap\Entry $entry
249
     * @return bool
250
     */
251
    public function updateEntry(Entry $entry): bool
252
    {
253
        try {
254
            $this->adapter->getEntryManager()->update($entry);
255
            return true;
256
        } catch (LdapException $e) {
257
            Logger::warning($e->getMessage());
258
            return false;
259
        }
260
    }
261
}
262