Passed
Push — 3.0 ( 5e1ed3...5a5495 )
by Rubén
04:22
created

LdapConnection::setServer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * sysPass
4
 *
5
 * @author    nuxsmin
6
 * @link      https://syspass.org
7
 * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
8
 *
9
 * This file is part of sysPass.
10
 *
11
 * sysPass is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * sysPass is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 *  along with sysPass.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace SP\Providers\Auth\Ldap;
26
27
use SP\Core\Events\Event;
28
use SP\Core\Events\EventDispatcher;
29
use SP\Core\Events\EventMessage;
30
31
32
/**
33
 * Class LdapConnection
34
 *
35
 * @package SP\Providers\Auth\Ldap
36
 */
37
final class LdapConnection implements LdapConnectionInterface
38
{
39
    const TIMEOUT = 10;
40
    /**
41
     * @var resource
42
     */
43
    private $ldapHandler;
44
    /**
45
     * @var LdapParams
46
     */
47
    private $ldapParams;
48
    /**
49
     * @var EventDispatcher
50
     */
51
    private $eventDispatcher;
52
    /**
53
     * @var bool
54
     */
55
    private $isConnected = false;
56
    /**
57
     * @var bool
58
     */
59
    private $isBound = false;
60
    /**
61
     * @var bool
62
     */
63
    private $isTls;
64
    /**
65
     * @var bool
66
     */
67
    private $debug;
68
    /**
69
     * @var string
70
     */
71
    private $server;
72
73
    /**
74
     * LdapBase constructor.
75
     *
76
     * @param LdapParams      $ldapParams
77
     * @param EventDispatcher $eventDispatcher
78
     * @param bool            $debug
79
     */
80
    public function __construct(LdapParams $ldapParams, EventDispatcher $eventDispatcher, $debug = false)
81
    {
82
        $this->ldapParams = $ldapParams;
83
        $this->eventDispatcher = $eventDispatcher;
84
        $this->debug = (bool)$debug;
85
    }
86
87
    /**
88
     * Comprobar la conexión al servidor de LDAP.
89
     *
90
     * @throws LdapException
91
     */
92
    public function checkConnection()
93
    {
94
        try {
95
            $this->connectAndBind();
96
97
            $this->eventDispatcher->notifyEvent('ldap.check.connection',
98
                new Event($this, EventMessage::factory()
99
                    ->addDescription(__u('LDAP connection OK')))
100
            );
101
        } catch (LdapException $e) {
102
            throw $e;
103
        }
104
    }
105
106
    /**
107
     * @return resource
108
     * @throws LdapException
109
     */
110
    public function connectAndBind()
111
    {
112
        if (!$this->isConnected && !$this->isBound) {
113
            $this->isConnected = $this->connect();
114
            $this->isBound = $this->bind();
115
        }
116
117
        return $this->ldapHandler;
118
    }
119
120
    /**
121
     * Realizar la conexión al servidor de LDAP.
122
     *
123
     * @throws LdapException
124
     * @return bool
125
     */
126
    public function connect(): bool
127
    {
128
        if ($this->isConnected) {
129
            return true;
130
        }
131
132
        $this->checkParams();
133
134
        // Habilitar la traza si el modo debug está habilitado
135
        if ($this->debug) {
136
            @ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ldap_set_option(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

136
            /** @scrutinizer ignore-unhandled */ @ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

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...
137
        }
138
139
        $this->ldapHandler = @ldap_connect($this->getServer(), $this->ldapParams->getPort());
140
141
        // Conexión al servidor LDAP
142
        if (!is_resource($this->ldapHandler)) {
143
            $this->eventDispatcher->notifyEvent('ldap.connect',
144
                new Event($this, EventMessage::factory()
145
                    ->addDescription(__u('Unable to connect to LDAP server'))
146
                    ->addDetail(__u('Server'), $this->getServer()))
147
            );
148
149
            throw new LdapException(__u('Unable to connect to LDAP server'));
150
        }
151
152
        @ldap_set_option($this->ldapHandler, LDAP_OPT_NETWORK_TIMEOUT, self::TIMEOUT);
153
        @ldap_set_option($this->ldapHandler, LDAP_OPT_PROTOCOL_VERSION, 3);
154
155
        $this->isTls = $this->connectTls();
156
157
        return true;
158
    }
159
160
    /**
161
     * Comprobar si los parámetros necesario de LDAP están establecidos.
162
     *
163
     * @throws LdapException
164
     */
165
    public function checkParams()
166
    {
167
        if (!$this->ldapParams->getSearchBase()
168
            || !$this->getServer()
169
            || !$this->ldapParams->getBindDn()
170
        ) {
171
            $this->eventDispatcher->notifyEvent('ldap.check.params',
172
                new Event($this, EventMessage::factory()
173
                    ->addDescription(__u('LDAP parameters are not set'))));
174
175
            throw new LdapException(__u('LDAP parameters are not set'));
176
        }
177
    }
178
179
    /**
180
     * @return string
181
     */
182
    public function getServer(): string
183
    {
184
        return $this->server ?: $this->ldapParams->getServer();
185
    }
186
187
    /**
188
     * @param string $server
189
     *
190
     * @return LdapConnection
191
     */
192
    public function setServer(string $server)
193
    {
194
        $this->server = $server;
195
196
        return $this;
197
    }
198
199
    /**
200
     * Connect through TLS
201
     *
202
     * @throws LdapException
203
     */
204
    private function connectTls(): bool
205
    {
206
        if ($this->ldapParams->isTlsEnabled()) {
207
            $result = @ldap_start_tls($this->ldapHandler);
208
209
            if ($result === false) {
210
                $this->eventDispatcher->notifyEvent('ldap.connect.tls',
211
                    new Event($this, EventMessage::factory()
212
                        ->addDescription(__u('Unable to connect to LDAP server'))
213
                        ->addDetail(__u('Server'), $this->getServer())
214
                        ->addDetail('TLS', __u('ON'))
215
                        ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler))));
216
217
                throw new LdapException(__u('Unable to connect to LDAP server'));
218
            }
219
220
            return true;
221
        }
222
223
        return false;
224
    }
225
226
    /**
227
     * Registrar error de LDAP y devolver el mensaje de error
228
     *
229
     * @param $ldapHandler
230
     *
231
     * @return string
232
     */
233
    public static function getLdapErrorMessage($ldapHandler)
234
    {
235
        return sprintf('%s (%d)', ldap_error($ldapHandler), ldap_errno($ldapHandler));
236
    }
237
238
    /**
239
     * Realizar la autentificación con el servidor de LDAP.
240
     *
241
     * @param string $bindDn   con el DN del usuario
242
     * @param string $bindPass con la clave del usuario
243
     *
244
     * @throws LdapException
245
     * @return bool
246
     */
247
    public function bind(string $bindDn = null, string $bindPass = null): bool
248
    {
249
        $dn = $bindDn ?: $this->ldapParams->getBindDn();
250
        $pass = $bindPass ?: $this->ldapParams->getBindPass();
251
252
        if (@ldap_bind($this->ldapHandler, $dn, $pass) === false) {
253
            $this->eventDispatcher->notifyEvent('ldap.bind',
254
                new Event($this, EventMessage::factory()
255
                    ->addDescription(__u('Connection error (BIND)'))
256
                    ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler))
257
                    ->addDetail('LDAP DN', $dn))
258
            );
259
260
            throw new LdapException(
261
                __u('Connection error (BIND)'),
262
                LdapException::ERROR,
263
                self::getLdapErrorMessage($this->ldapHandler),
264
                $this->getErrorCode()
265
            );
266
        }
267
268
        return true;
269
    }
270
271
    /**
272
     * @return int
273
     */
274
    public function getErrorCode()
275
    {
276
        if (is_resource($this->ldapHandler)) {
277
            return ldap_errno($this->ldapHandler);
278
        }
279
280
        return -1;
281
    }
282
283
    /**
284
     * @return bool
285
     */
286
    public function isConnected(): bool
287
    {
288
        return $this->isConnected;
289
    }
290
291
    /**
292
     * @return bool
293
     */
294
    public function isBound(): bool
295
    {
296
        return $this->isBound;
297
    }
298
299
    /**
300
     * @return LdapParams
301
     */
302
    public function getLdapParams(): LdapParams
303
    {
304
        return $this->ldapParams;
305
    }
306
307
    /**
308
     * @return bool
309
     */
310
    public function isDebug(): bool
311
    {
312
        return $this->debug;
313
    }
314
315
    /**
316
     * Realizar la desconexión del servidor de LDAP.
317
     */
318
    public function unbind(): bool
319
    {
320
        if (($this->isConnected || $this->isBound)
321
            && @ldap_unbind($this->ldapHandler) === false
322
        ) {
323
            $this->eventDispatcher->notifyEvent('ldap.unbind',
324
                new Event($this, EventMessage::factory()
325
                    ->addDescription(__u('Error while disconnecting from LDAP server'))
326
                    ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler)))
327
            );
328
329
            return false;
330
        }
331
332
        return true;
333
    }
334
}