AttributeAddFromLDAP::process()   F
last analyzed

Complexity

Conditions 20
Paths 129

Size

Total Lines 95
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 57
c 1
b 0
f 0
dl 0
loc 95
rs 3.925
cc 20
nc 129
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Filter to add attributes to the identity by executing a query against an LDAP directory
5
 *
6
 * @package simplesamlphp/simplesamlphp-module-ldap
7
 */
8
9
declare(strict_types=1);
10
11
namespace SimpleSAML\Module\ldap\Auth\Process;
12
13
use SimpleSAML\Assert\Assert;
14
use SimpleSAML\Logger;
15
use Symfony\Component\Ldap\Adapter\ExtLdap\Query;
16
17
use function array_values;
18
19
class AttributeAddFromLDAP extends BaseFilter
20
{
21
    /**
22
     * LDAP attributes to add to the request attributes
23
     *
24
     * @var string[]
25
     */
26
    protected array $searchAttributes;
27
28
    /**
29
     * LDAP attributes to base64 encode
30
     *
31
     * @var string[]
32
     */
33
    protected array $binaryAttributes;
34
35
    /**
36
     * LDAP search filter to use in the LDAP query
37
     *
38
     * @var string
39
     */
40
    protected string $searchFilter;
41
42
    /**
43
     * What to do with attributes when the target already exists. Either replace, merge or add.
44
     *
45
     * @var string
46
     */
47
    protected string $attrPolicy;
48
49
    /** @var string|null */
50
    protected ?string $searchUsername;
51
52
    /** @var string|null */
53
    protected ?string $searchPassword;
54
55
    /**
56
     * Initialize this filter.
57
     *
58
     * @param array<mixed> $config Configuration information about this filter.
59
     * @param mixed $reserved For future use.
60
     */
61
    public function __construct(array $config, $reserved)
62
    {
63
        parent::__construct($config, $reserved);
64
65
        // Get filter specific config options
66
        $this->binaryAttributes = $this->config->getOptionalArray('attributes.binary', []);
67
        $this->searchAttributes = $this->config->getOptionalArrayize('attributes', []);
68
        if (empty($this->searchAttributes)) {
69
            $new_attribute = $this->config->getString('attribute.new');
70
            $this->searchAttributes[$new_attribute] = $this->config->getString('search.attribute');
71
        }
72
        $this->searchFilter = $this->config->getString('search.filter');
73
74
        // get the attribute policy
75
        $this->attrPolicy = $this->config->getOptionalString('attribute.policy', 'merge');
76
        Assert::oneOf($this->attrPolicy, ['merge', 'replace', 'add']);
77
78
        $this->searchUsername = $this->config->getOptionalString('search.username', null);
79
        $this->searchPassword = $this->config->getOptionalString('search.password', null);
80
    }
81
82
83
    /**
84
     * Add attributes from an LDAP server.
85
     *
86
     * @param array<mixed> &$state The current request
87
     */
88
    public function process(array &$state): void
89
    {
90
        Assert::keyExists($state, 'Attributes');
91
        $attributes = &$state['Attributes'];
92
93
        // perform a merge on the ldap_search_filter
94
        // loop over the attributes and build the search and replace arrays
95
        $arrSearch = $arrReplace = [];
96
        foreach ($attributes as $attr => $val) {
97
            $arrSearch[] = '%' . $attr . '%';
98
99
            if (is_array($val) && count($val) > 0 && is_string($val[0]) && strlen($val[0]) > 0) {
100
                $arrReplace[] = $this->connector->escapeFilterValue($val[0], true);
0 ignored issues
show
Bug introduced by
The method escapeFilterValue() does not exist on SimpleSAML\Module\ldap\ConnectorInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to SimpleSAML\Module\ldap\ConnectorInterface. ( Ignorable by Annotation )

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

100
                /** @scrutinizer ignore-call */ 
101
                $arrReplace[] = $this->connector->escapeFilterValue($val[0], true);
Loading history...
101
            } else {
102
                $arrReplace[] = '';
103
            }
104
        }
105
106
        // merge the attributes into the ldap_search_filter
107
        /** @psalm-var string[] $arrReplace */
108
        $filter = str_replace($arrSearch, $arrReplace, $this->searchFilter);
109
        if (strpos($filter, '%') !== false) {
110
            Logger::info(sprintf(
111
                '%s: There are non-existing attributes in the search filter. (%s)',
112
                $this->title,
113
                $filter,
114
            ));
115
            return;
116
        }
117
118
        $this->connector->bind($this->searchUsername, $this->searchPassword);
119
120
        $wantedAttrs = $this->config->getOptionalValue(
121
            'attributes',
122
            // If specifically set to NULL return all attributes, if not set at all return nothing (safe default)
123
            in_array('attributes', $this->config->getOptions(), true) ? ['*'] : [],
124
        );
125
126
        $options = [
127
            'scope' => $this->config->getOptionalString('search.scope', Query::SCOPE_SUB),
128
            'timeout' => $this->config->getOptionalInteger('timeout', 3),
129
            'filter' => array_values($wantedAttrs),
130
        ];
131
132
        $entries = $this->connector->searchForMultiple(
133
            $this->searchBase,
134
            $filter,
135
            $options,
136
            true,
137
        );
138
139
        $results = [];
140
        foreach ($entries as $entry) {
141
            $tmp = array_intersect_key(
142
                $entry->getAttributes(),
143
                array_fill_keys(array_values($this->searchAttributes), null),
144
            );
145
146
            $binaries = array_intersect(
147
                array_keys($tmp),
148
                $this->binaryAttributes,
149
            );
150
            foreach ($binaries as $binary) {
151
                $attr = $entry->getAttribute($binary);
152
                $tmp[$binary] = array_map('base64_encode', $attr);
0 ignored issues
show
Bug introduced by
It seems like $attr can also be of type null; however, parameter $array 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

152
                $tmp[$binary] = array_map('base64_encode', /** @scrutinizer ignore-type */ $attr);
Loading history...
153
            }
154
155
            $results[] = $tmp;
156
        }
157
158
        // handle [multiple] values
159
        foreach ($results as $result) {
160
            foreach ($this->searchAttributes as $target => $name) {
161
                // If there is no mapping defined, just use the name of the LDAP-attribute as a target
162
                if (is_int($target)) {
163
                    $target = $name;
164
                }
165
166
                if (isset($attributes[$target]) && $this->attrPolicy === 'replace') {
167
                    unset($attributes[$target]);
168
                }
169
170
                if (isset($result[$name])) {
171
                    if (isset($attributes[$target])) {
172
                        foreach (array_values($result[$name]) as $value) {
173
                            if ($this->attrPolicy === 'merge') {
174
                                if (!in_array($value, $attributes[$target], true)) {
175
                                    $attributes[$target][] = $value;
176
                                }
177
                            } else {
178
                                $attributes[$target][] = $value;
179
                            }
180
                        }
181
                    } else {
182
                        $attributes[$target] = array_values($result[$name]);
183
                    }
184
                }
185
            }
186
        }
187
    }
188
}
189