Passed
Push — devel-3.0 ( 5a06ca...84adb8 )
by Rubén
03:54
created

LdapActions::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
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 LdapActions
34
 *
35
 * @package SP\Providers\Auth\Ldap
36
 */
37
final class LdapActions
38
{
39
    /**
40
     * Atributos de búsqueda
41
     */
42
    const USER_ATTRIBUTES = [
43
        'dn',
44
        'displayname',
45
        'samaccountname',
46
        'mail',
47
        'memberof',
48
        'lockouttime',
49
        'fullname',
50
        'groupmembership',
51
        'uid',
52
        'givenname',
53
        'sn',
54
        'userprincipalname',
55
        'cn'
56
    ];
57
58
    /**
59
     * @var LdapParams
60
     */
61
    private $ldapParams;
62
    /**
63
     * @var resource
64
     */
65
    private $ldapHandler;
66
    /**
67
     * @var EventDispatcher
68
     */
69
    private $eventDispatcher;
70
71
    /**
72
     * LdapActions constructor.
73
     *
74
     * @param LdapConnection  $ldapConnection
75
     * @param EventDispatcher $eventDispatcher
76
     *
77
     * @throws LdapException
78
     */
79
    public function __construct(LdapConnection $ldapConnection, EventDispatcher $eventDispatcher)
80
    {
81
        $this->ldapHandler = $ldapConnection->connectAndBind();
82
        $this->ldapParams = $ldapConnection->getLdapParams();
83
        $this->eventDispatcher = $eventDispatcher;
84
    }
85
86
    /**
87
     * Obtener el RDN del grupo.
88
     *
89
     * @param string $groupFilter
90
     *
91
     * @return array Groups' DN
92
     * @throws LdapException
93
     */
94
    public function searchGroupsDn(string $groupFilter)
95
    {
96
        $filter = '(&(cn='
97
            . ldap_escape($this->getGroupFromParams(), null, LDAP_ESCAPE_FILTER)
98
            . ')'
99
            . $groupFilter
100
            . ')';
101
102
        $searchResults = $this->getResults($filter, ['dn']);
103
104
        if ((int)$searchResults['count'] === 0) {
105
            $this->eventDispatcher->notifyEvent('ldap.search.group',
106
                new Event($this, EventMessage::factory()
107
                    ->addDescription(__u('Error al buscar RDN de grupo'))
108
                    ->addDetail(__u('Grupo'), $this->getGroupFromParams())
109
                    ->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler))
110
                    ->addDetail('LDAP FILTER', $filter))
111
            );
112
113
            throw new LdapException(__u('Error al buscar RDN de grupo'));
114
        }
115
116
        return array_filter(array_map(function ($value) {
117
            if (is_array($value)) {
118
                return $value['dn'];
119
            }
120
121
            return null;
122
        }, $searchResults));
0 ignored issues
show
Bug introduced by
It seems like $searchResults can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

122
        }, /** @scrutinizer ignore-type */ $searchResults));
Loading history...
123
    }
124
125
    /**
126
     * @return string
127
     */
128
    protected function getGroupFromParams(): string
129
    {
130
        if (strpos($this->ldapParams->getGroup(), 'cn') === 0) {
131
            return LdapUtil::getGroupName($this->ldapParams->getGroup());
0 ignored issues
show
Bug Best Practice introduced by
The expression return SP\Providers\Auth...ldapParams->getGroup()) returns the type boolean which is incompatible with the type-hinted return string.
Loading history...
132
        }
133
134
        return $this->ldapParams->getGroup();
135
    }
136
137
    /**
138
     * Devolver los resultados de una paginación
139
     *
140
     * @param string $filter     Filtro a utilizar
141
     * @param array  $attributes Atributos a devolver
142
     *
143
     * @return bool|array
144
     */
145
    protected function getResults($filter, array $attributes = null)
146
    {
147
        $cookie = '';
148
        $results = [];
149
150
        do {
151
            ldap_control_paged_result($this->ldapHandler, 1000, false, $cookie);
152
153
            if (!$searchRes = @ldap_search($this->ldapHandler, $this->ldapParams->getSearchBase(), $filter, $attributes)) {
154
                return false;
155
            }
156
157
            if (@ldap_count_entries($this->ldapHandler, $searchRes) === 0
158
                || !$entries = @ldap_get_entries($this->ldapHandler, $searchRes)
159
            ) {
160
                return false;
161
            }
162
163
            $results = array_merge($results, $entries);
164
165
            ldap_control_paged_result_response($this->ldapHandler, $searchRes, $cookie);
166
        } while (!empty($cookie));
167
168
        return $results;
169
    }
170
171
    /**
172
     * Obtener los atributos del usuario.
173
     *
174
     * @param string $filter
175
     *
176
     * @return array
177
     * @throws LdapException
178
     */
179
    public function getAttributes(string $filter)
180
    {
181
        $validAttributes = [
182
            'dn' => 'dn',
183
            'groupmembership' => 'group',
184
            'memberof' => 'group',
185
            'displayname' => 'fullname',
186
            'fullname' => 'fullname',
187
            'givenname' => 'name',
188
            'sn' => 'sn',
189
            'mail' => 'mail',
190
            'lockouttime' => 'expire'
191
        ];
192
193
        $res = [
194
            'dn' => '',
195
            'name' => '',
196
            'sn' => '',
197
            'mail' => '',
198
            'group' => [],
199
            'expire' => 0
200
        ];
201
202
        $searchResults = $this->getObjects($filter);
203
204
        if ((int)$searchResults['count'] === 0) {
205
            $this->eventDispatcher->notifyEvent('ldap.getAttributes',
206
                new Event($this, EventMessage::factory()
207
                    ->addDescription(__u('Error al localizar el usuario en LDAP'))
208
                    ->addDetail('LDAP FILTER', $filter))
209
            );
210
211
            throw new LdapException(__u('Error al localizar el usuario en LDAP'));
212
        }
213
214
        foreach ($searchResults as $result) {
215
            if (is_array($result)) {
216
                foreach ($result as $attribute => $values) {
217
                    $normalizedAttribute = strtolower($attribute);
218
219
                    if (array_key_exists($normalizedAttribute, $validAttributes)) {
220
                        if (is_array($values)) {
221
                            $count = (int)$values['count'];
222
223
                            if ($count > 1) {
224
                                unset($values['count']);
225
226
                                $res[$validAttributes[$normalizedAttribute]] = $values;
227
                            } else {
228
                                // Almacenamos  1 solo valor
229
                                $res[$validAttributes[$normalizedAttribute]] = trim($values[0]);
230
                            }
231
                        } else {
232
                            $res[$validAttributes[$normalizedAttribute]] = trim($values);
233
                        }
234
                    }
235
                }
236
            }
237
        }
238
239
        return $res;
240
    }
241
242
    /**
243
     * Obtener los objetos según el filtro indicado
244
     *
245
     * @param string $filter
246
     * @param array  $attributes
247
     *
248
     * @return array
249
     * @throws LdapException
250
     */
251
    public function getObjects($filter, array $attributes = self::USER_ATTRIBUTES)
252
    {
253
        if (($searchResults = $this->getResults($filter, $attributes)) === false) {
254
            $this->eventDispatcher->notifyEvent('ldap.search',
255
                new Event($this, EventMessage::factory()
256
                    ->addDescription(__u('Error al buscar objetos en DN base'))
257
                    ->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler))
258
                    ->addDetail('LDAP FILTER', $filter))
259
            );
260
261
            throw new LdapException(__u('Error al buscar objetos en DN base'));
262
        }
263
264
        return $searchResults;
265
    }
266
}