Passed
Push — master ( 3896ee...e50616 )
by Raffael
05:45
created

Ldap::prepareRawObject()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.3906

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 9
cts 12
cp 0.75
rs 9.3222
c 0
b 0
f 0
cc 5
nc 5
nop 1
crap 5.3906
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * tubee.io
7
 *
8
 * @copyright   Copryright (c) 2017-2019 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Tubee\Endpoint;
13
14
use Dreamscapes\Ldap\Core\Ldap as LdapServer;
15
use Generator;
16
use InvalidArgumentException;
17
use Psr\Log\LoggerInterface;
18
use Tubee\AttributeMap\AttributeMapInterface;
19
use Tubee\Collection\CollectionInterface;
20
use Tubee\Endpoint\Ldap\Exception as LdapEndpointException;
21
use Tubee\Endpoint\Ldap\QueryTransformer;
22
use Tubee\EndpointObject\EndpointObjectInterface;
23
use Tubee\Workflow\Factory as WorkflowFactory;
24
25
class Ldap extends AbstractEndpoint
26
{
27
    /**
28
     * Kind.
29
     */
30
    public const KIND = 'LdapEndpoint';
31
32
    /**
33
     * Ldap.
34
     *
35
     * @var Ldap
36
     */
37
    protected $ldap;
38
39
    /**
40
     * Uri.
41
     *
42
     * @var string
43
     */
44
    protected $uri = 'ldap://127.0.0.1:389';
45
46
    /**
47
     * Binddn.
48
     *
49
     * @var string
50
     */
51
    protected $binddn;
52
53
    /**
54
     * Bindpw.
55
     *
56
     * @var string
57
     */
58
    protected $bindpw;
59
60
    /**
61
     * Basedn.
62
     *
63
     * @var string
64
     */
65
    protected $basedn = '';
66
67
    /**
68
     * tls.
69
     *
70
     * @var bool
71
     */
72
    protected $tls = false;
73
74
    /**
75
     *  Options.
76
     *
77
     * @var array
78
     */
79
    protected $options = [];
80
81
    /**
82
     * Init endpoint.
83
     */
84 29
    public function __construct(string $name, string $type, LdapServer $ldap, CollectionInterface $collection, WorkflowFactory $workflow, LoggerInterface $logger, array $resource = [])
85
    {
86 29
        $this->ldap = $ldap;
0 ignored issues
show
Documentation Bug introduced by
It seems like $ldap of type object<Dreamscapes\Ldap\Core\Ldap> is incompatible with the declared type object<Tubee\Endpoint\Ldap> of property $ldap.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
87 29
        $this->identifier = 'entrydn';
88
89 29
        if (isset($resource['data']['resource'])) {
90
            $this->setLdapOptions($resource['data']['resource']);
91
        }
92
93 29
        parent::__construct($name, $type, $collection, $workflow, $logger, $resource);
94 29
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 4
    public function setup(bool $simulate = false): EndpointInterface
100
    {
101 4
        $this->logger->debug('connect to ldap server ['.$this->uri.']', [
102 4
            'category' => get_class($this),
103
        ]);
104
105 4
        if (null === $this->binddn) {
106 3
            $this->logger->warning('no binddn set for ldap connection, you should avoid anonymous bind', [
107 3
                'category' => get_class($this),
108
            ]);
109
        }
110
111 4
        if (false === $this->tls && 'ldaps' !== substr($this->uri, 0, 5)) {
112 3
            $this->logger->warning('neither tls nor ldaps enabled for ldap connection, it is strongly reccommended to encrypt ldap connections', [
113 3
                'category' => get_class($this),
114
            ]);
115
        }
116
117 4
        $this->ldap->connect($this->uri);
0 ignored issues
show
Bug introduced by
The method connect() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
118
119 4
        foreach ($this->options as $opt => $value) {
120 1
            $this->ldap->setOption(constant($opt), $value);
0 ignored issues
show
Bug introduced by
The method setOption() does not exist on Tubee\Endpoint\Ldap. Did you maybe mean setOptions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
121
        }
122
123 4
        if (true === $this->tls) {
124 1
            $this->ldap->startTls();
0 ignored issues
show
Bug introduced by
The method startTls() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
125
        }
126
127 4
        $this->logger->info('bind to ldap server ['.$this->uri.'] with binddn ['.$this->binddn.']', [
128 4
            'category' => get_class($this),
129
        ]);
130
131 4
        $this->ldap->bind($this->binddn, $this->bindpw);
0 ignored issues
show
Bug introduced by
The method bind() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
132
133 4
        return $this;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 3
    public function setLdapOptions(?array $config = null): EndpointInterface
140
    {
141 3
        if ($config === null) {
142
            return $this;
143
        }
144
145 3
        foreach ($config as $option => $value) {
146 3
            switch ($option) {
147 3
                case 'options':
148 1
                    $this->options = $value;
149
150 1
                    break;
151 2
                case 'uri':
152 2
                case 'binddn':
153 1
                case 'bindpw':
154 1
                case 'basedn':
155 1
                    $this->{$option} = (string) $value;
156
157 1
                    break;
158 1
                case 'tls':
159 1
                    $this->tls = (bool) $value;
160
161 1
                    break;
162
                default:
163 3
                    throw new InvalidArgumentException('unknown ldap option '.$option.' given');
164
            }
165
        }
166
167 3
        return $this;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173 1
    public function shutdown(bool $simulate = false): EndpointInterface
174
    {
175 1
        $this->ldap->close();
0 ignored issues
show
Bug introduced by
The method close() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
176
177 1
        return $this;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 3
    public function change(AttributeMapInterface $map, array $diff, array $object, array $endpoint_object, bool $simulate = false): ?string
184
    {
185 3
        $object = array_change_key_case($object);
186 3
        $dn = $this->getDn($object, $endpoint_object);
187
188 3
        if (isset($diff['entrydn'])) {
189
            unset($diff['entrydn']);
190
        }
191
192 3
        $this->logger->info('update ldap object ['.$dn.'] on endpoint ['.$this->getIdentifier().'] with attributes [{attributes}]', [
193 3
            'category' => get_class($this),
194 3
            'attributes' => $diff,
195
        ]);
196
197 3
        if ($dn !== $endpoint_object['entrydn']) {
198 1
            $this->moveLdapObject($dn, $endpoint_object['entrydn'], $simulate);
199 1
            $rdn_attr = explode('=', $dn);
200 1
            $rdn_attr = strtolower(array_shift($rdn_attr));
201
202 1
            if (isset($diff[$rdn_attr])) {
203
                unset($diff[$rdn_attr]);
204
            }
205
        }
206
207 3
        if ($simulate === false) {
208 2
            $this->ldap->modifyBatch($dn, $diff);
0 ignored issues
show
Bug introduced by
The method modifyBatch() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
209
210 2
            return $dn;
211
        }
212
213 1
        return null;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219 2
    public function delete(AttributeMapInterface $map, array $object, array $endpoint_object, bool $simulate = false): bool
220
    {
221 2
        $dn = $this->getDn($object, $endpoint_object);
222 2
        $this->logger->debug('delete ldap object ['.$dn.']', [
223 2
            'category' => get_class($this),
224
        ]);
225
226 2
        if ($simulate === false) {
227 1
            $this->ldap->delete($dn);
0 ignored issues
show
Bug introduced by
The call to delete() misses some required arguments starting with $object.
Loading history...
Documentation introduced by
$dn is of type string, but the function expects a object<Tubee\AttributeMap\AttributeMapInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
        }
229
230 2
        return true;
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236 3
    public function create(AttributeMapInterface $map, array $object, bool $simulate = false): ?string
237
    {
238 3
        $dn = $this->getDn($object);
239
240 2
        if (isset($object['entrydn'])) {
241 2
            unset($object['entrydn']);
242
        }
243
244 2
        $this->logger->info('create new ldap object ['.$dn.'] on endpoint ['.$this->getIdentifier().'] with attributes [{attributes}]', [
245 2
            'category' => get_class($this),
246 2
            'attributes' => $object,
247
        ]);
248
249 2
        if ($simulate === false) {
250 1
            $this->ldap->add($dn, $object);
0 ignored issues
show
Bug introduced by
The method add() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
251
252 1
            return $dn;
253
        }
254
255 1
        return null;
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261 8
    public function transformQuery(?array $query = null)
262
    {
263 8
        $result = null;
264
265 8
        if ($this->filter_all !== null) {
266 1
            $result = $this->filter_all;
267
        }
268
269 8
        if (!empty($query)) {
270 8
            if ($this->filter_all === null) {
271 7
                $result = QueryTransformer::transform($query);
272
            } else {
273 1
                $result = '(&'.$this->filter_all.QueryTransformer::transform($query).')';
274
            }
275
        }
276
277 8
        return $result;
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function getAll(?array $query = null): Generator
284
    {
285
        $filter = $this->transformQuery($query);
286
        $this->logger->debug('find all ldap objects with ldap filter ['.$filter.'] on endpoint ['.$this->name.']', [
287
            'category' => get_class($this),
288
        ]);
289
290
        $i = 0;
291
        $result = $this->ldap->ldapSearch($this->basedn, $filter);
0 ignored issues
show
Bug introduced by
The method ldapSearch() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
292
        foreach ($result->getEntries() as $object) {
293
            yield $this->build($object);
294
        }
295
296
        return $i;
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302 5
    public function getDiff(AttributeMapInterface $map, array $diff): array
303
    {
304 5
        $result = [];
305 5
        foreach ($diff as $attribute => $update) {
306 4
            switch ($update['action']) {
307 4
                case AttributeMapInterface::ACTION_REPLACE:
308 2
                    $result[] = [
309 2
                        'attrib' => $attribute,
310 2
                        'modtype' => LDAP_MODIFY_BATCH_REPLACE,
311 2
                        'values' => (array) $update['value'],
312
                    ];
313
314 2
                break;
315 2
                case AttributeMapInterface::ACTION_REMOVE:
316 1
                    $result[] = [
317 1
                        'attrib' => $attribute,
318 1
                        'modtype' => LDAP_MODIFY_BATCH_REMOVE_ALL,
319
                    ];
320
321 1
                break;
322 1
                case AttributeMapInterface::ACTION_ADD:
323 1
                    $result[] = [
324 1
                        'attrib' => $attribute,
325 1
                        'modtype' => LDAP_MODIFY_BATCH_ADD,
326 1
                        'values' => (array) $update['value'],
327
                    ];
328
329 1
                break;
330
                default:
331 4
                    throw new InvalidArgumentException('unknown diff action '.$update['action'].' given');
332
            }
333
        }
334
335 5
        return $result;
336
    }
337
338
    /**
339
     * {@inheritdoc}
340
     */
341 3
    public function getOne(array $object, ?array $attributes = []): EndpointObjectInterface
342
    {
343 3
        $filter = $this->getFilterOne($object);
344 3
        $this->logger->debug('find ldap object with ldap filter ['.$filter.'] in ['.$this->basedn.'] on endpoint ['.$this->getIdentifier().']', [
345 3
            'category' => get_class($this),
346
        ]);
347
348 3
        $result = $this->ldap->ldapSearch($this->basedn, $filter, $attributes);
0 ignored issues
show
Bug introduced by
The method ldapSearch() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
349 3
        $count = $result->countEntries();
350
351 3
        if ($count > 1) {
352 1
            throw new Exception\ObjectMultipleFound('found more than one object with filter '.$filter);
353
        }
354 2
        if ($count === 0) {
355 1
            throw new Exception\ObjectNotFound('no object found with filter '.$filter);
356
        }
357
358 1
        return $this->build($this->prepareRawObject($result->getEntries()[0]));
359
    }
360
361
    /**
362
     * Prepare object.
363
     */
364 1
    protected function prepareRawObject(array $result): array
365
    {
366 1
        $object = [];
367 1
        foreach ($result as $key => $attr) {
368 1
            if ($key === 'dn') {
369
                $object['entrydn'] = $attr;
370 1
            } elseif (!is_int($key)) {
371 1
                if ($attr['count'] === 1) {
372 1
                    $object[$key] = $attr[0];
373
                } else {
374
                    $val = $attr;
375
                    unset($val['count']);
376 1
                    $object[$key] = $val;
377
                }
378
            }
379
        }
380
381 1
        return $object;
382
    }
383
384
    /**
385
     * Move ldap object.
386
     */
387 1
    protected function moveLdapObject(string $new_dn, string $current_dn, bool $simulate = false): bool
388
    {
389 1
        $this->logger->info('found object ['.$current_dn.'] but is not at the expected place ['.$new_dn.'], move object', [
390 1
            'category' => get_class($this),
391
        ]);
392
393 1
        $result = explode(',', $new_dn);
394 1
        $rdn = array_shift($result);
395 1
        $parent_dn = implode(',', $result);
396
397 1
        if ($simulate === false) {
398 1
            $this->ldap->rename($current_dn, $rdn, $parent_dn, true);
0 ignored issues
show
Bug introduced by
The method rename() does not seem to exist on object<Tubee\Endpoint\Ldap>.

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...
399
        }
400
401 1
        return true;
402
    }
403
404
    /**
405
     * Get dn.
406
     */
407 8
    protected function getDn(array $object, array $endpoint_object = []): string
408
    {
409 8
        if (isset($object['entrydn'])) {
410 7
            return $object['entrydn'];
411
        }
412 1
        if (isset($endpoint_object['entrydn'])) {
413
            return $endpoint_object['entrydn'];
414
        }
415
416 1
        throw new LdapEndpointException\NoEntryDn('no attribute entrydn found in data object');
417
    }
418
}
419