Passed
Pull Request — master (#62)
by
unknown
17:52
created

Ldap::saslBind()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 13
rs 10
cc 3
nc 3
nop 7
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 $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->resolveBindError($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(?string $username, #[\SensitiveParameter]?string $password, ?string $mech, ?string $realm, ?string $authcId, ?string $authzId, ?string $props): void
116
117
    {
118
        try {
119
            $this->connection->saslBind($username, strval($password), $mech, $realm, $authcId, $authzId, $props);
0 ignored issues
show
Bug introduced by
The method saslBind() does not exist on Symfony\Component\Ldap\Ldap. ( Ignorable by Annotation )

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

119
            $this->connection->/** @scrutinizer ignore-call */ 
120
                               saslBind($username, strval($password), $mech, $realm, $authcId, $authzId, $props);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
120
        } catch (InvalidCredentialsException $e) {
121
            throw new Error\Error($this->resolveBindError($e));
122
        }
123
124
        if ($username === null) {
125
            Logger::debug("LDAP bind(): Anonymous bind succesful.");
126
        } else {
127
            Logger::debug(sprintf("LDAP bind(): Bind successful for DN '%s'.", $username));
128
        }
129
    }
130
131
    /**
132
     * @inheritDoc
133
     */
134
    public function whoami(): string
135
    {
136
        return $this->connection->whoami();
0 ignored issues
show
Bug introduced by
The method whoami() does not exist on Symfony\Component\Ldap\Ldap. ( Ignorable by Annotation )

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

136
        return $this->connection->/** @scrutinizer ignore-call */ whoami();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
137
    }
138
139
    /**
140
     * @inheritDoc
141
     */
142
    public function search(
143
        array $searchBase,
144
        string $filter,
145
        array $options,
146
        bool $allowMissing,
147
    ): ?Entry {
148
        $entry = null;
149
150
        foreach ($searchBase as $base) {
151
            $query  = $this->connection->query($base, $filter, $options);
152
            $result = $query->execute()->toArray();
153
154
            if (count($result) > 1) {
155
                throw new Error\Exception(sprintf(
156
                    "LDAP search(): Found %d entries searching base '%s' for '%s'",
157
                    count($result),
158
                    $base,
159
                    $filter,
160
                ));
161
            } elseif (count($result) === 1) {
162
                $entry = array_pop($result);
163
                break;
164
            } else {
165
                Logger::debug(sprintf(
166
                    "LDAP search(): Found no entries searching base '%s' for '%s'",
167
                    $base,
168
                    $filter,
169
                ));
170
            }
171
        }
172
173
        if ($entry === null && $allowMissing === false) {
174
            throw new Error\Exception(sprintf(
175
                "Object not found using search base [%s] and filter '%s'",
176
                implode(', ', $searchBase),
177
                $filter,
178
            ));
179
        }
180
181
        return $entry;
182
    }
183
184
185
    /**
186
     * @inheritDoc
187
     */
188
    public function searchForMultiple(
189
        array $searchBase,
190
        string $filter,
191
        array $options,
192
        bool $allowMissing,
193
    ): array {
194
        $results = [];
195
196
        foreach ($searchBase as $base) {
197
            $query   = $this->connection->query($base, $filter, $options);
198
            $result  = $query->execute()->toArray();
199
            $results = array_merge($results, $result);
200
201
            Logger::debug(sprintf(
202
                "Library - LDAP search(): Found %d entries searching base '%s' for '%s'",
203
                count($result),
204
                $base,
205
                $filter,
206
            ));
207
        }
208
209
        if (empty($results) && ($allowMissing === false)) {
210
            throw new Error\Exception(sprintf(
211
                "No Objects found using search base [%s] and filter '%s'",
212
                implode(', ', $searchBase),
213
                $filter,
214
            ));
215
        }
216
217
        return $results;
218
    }
219
220
221
    /**
222
     * Resolve the message to a UI exception
223
     *
224
     * @param InvalidCredentialsException $e
225
     * @return string
226
     */
227
    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

227
    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...
228
    {
229
        return self::ERR_WRONG_PASS;
230
    }
231
232
233
    /**
234
     * @param \Symfony\Component\Ldap\Entry $entry
235
     * @return bool
236
     */
237
    public function updateEntry(Entry $entry): bool
238
    {
239
        try {
240
            $this->adapter->getEntryManager()->update($entry);
241
            return true;
242
        } catch (LdapException $e) {
243
            Logger::warning($e->getMessage());
244
            return false;
245
        }
246
    }
247
}
248