Passed
Pull Request — master (#24)
by
unknown
07:13
created

AttributeAddFromLDAP   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 74
c 2
b 0
f 1
dl 0
loc 165
rs 10
wmc 21

2 Methods

Rating   Name   Duplication   Size   Complexity  
D process() 0 95 19
A __construct() 0 17 2
1
<?php
2
3
/**
4
 * Filter to add attributes to the identity by executing a query against an LDAP directory
5
 *
6
 * Original Author: Steve Moitozo II <[email protected]>
7
 * Created: 20100513
8
 * Updated: 20100920 Steve Moitozo II
9
 *          - incorporated feedback from Olav Morken to prep code for inclusion in SimpleSAMLphp distro
10
 *          - moved call to ldap_set_options() inside test for $ds
11
 *          - added the output of ldap_error() to the exceptions
12
 *          - reduced some of the nested ifs
13
 *          - added support for multiple values
14
 *          - added support for anonymous binds
15
 *          - added escaping of search filter and attribute
16
 * Updated: 20111118 Ryan Panning
17
 *          - Updated the class to use BaseFilter which reuses LDAP connection features
18
 *          - Added conversion of original filter option names for backwards-compatibility
19
 *          - Updated the constructor to use the new config method
20
 *          - Updated the process method to use the new config variable names
21
 * Updated: 20131119 Yørn de Jong / Jaime Perez
22
 *          - Added support for retrieving multiple values at once from LDAP
23
 *          - Don't crash but fail silently on LDAP errors; the plugin is to complement attributes
24
 * Updated: 20161223 Remy Blom <[email protected]>
25
 *          - Adjusted the silent fail so it does show a warning in log when $this->getLdap() fails
26
 *
27
 * @package SimpleSAMLphp
28
 */
29
30
declare(strict_types=1);
31
32
namespace SimpleSAML\Module\ldap\Auth\Process;
33
34
use Exception;
35
use SimpleSAML\Assert\Assert;
36
use SimpleSAML\Logger;
37
use SimpleSAML\Module\ldap\Auth\Ldap;
38
39
class AttributeAddFromLDAP extends BaseFilter
40
{
41
    /**
42
     * LDAP attributes to add to the request attributes
43
     *
44
     * @var array
45
     */
46
    protected array $search_attributes;
47
48
    /**
49
     * LDAP attributes to base64 encode
50
     *
51
     * @var array
52
     */
53
    protected array $binary_attributes;
54
55
    /**
56
     * LDAP search filter to use in the LDAP query
57
     *
58
     * @var string
59
     */
60
    protected string $search_filter;
61
62
    /**
63
     * What to do with attributes when the target already exists. Either replace, merge or add.
64
     *
65
     * @var string
66
     */
67
    protected string $attr_policy;
68
69
70
    /**
71
     * Only run when none of these attributes are set. Empty array implies always run.
72
     *
73
     * @var array
74
     */
75
    protected array $if_missing_attributes;
76
77
78
    /**
79
     * Initialize this filter.
80
     *
81
     * @param array $config Configuration information about this filter.
82
     * @param mixed $reserved For future use.
83
     */
84
    public function __construct(array $config, $reserved)
85
    {
86
        parent::__construct($config, $reserved);
87
88
        // Get filter specific config options
89
        $this->binary_attributes = $this->config->getArray('attributes.binary', []);
90
        $this->search_attributes = $this->config->getArrayize('attributes', []);
91
        if (empty($this->search_attributes)) {
92
            $new_attribute = $this->config->getString('attribute.new', '');
93
            $this->search_attributes[$new_attribute] = $this->config->getString('search.attribute');
94
        }
95
        $this->search_filter = $this->config->getString('search.filter');
96
97
        // get the attribute policy
98
        $this->attr_policy = $this->config->getString('attribute.policy', 'merge');
99
100
        $this->if_missing_attributes = $this->config->getArrayize('if.missing.attributes', []);
101
    }
102
103
104
    /**
105
     * Add attributes from an LDAP server.
106
     *
107
     * @param array &$request The current request
108
     */
109
    public function process(array &$request): void
110
    {
111
        Assert::keyExists($request, 'Attributes');
112
113
        $attributes = &$request['Attributes'];
114
115
        // skip if any of if.missing.attributes is set
116
        foreach($this->if_missing_attributes as $attribute) {
117
            if (isset($attributes[$attribute])) {
118
                \SimpleSAML\Logger::debug(
119
                    'AttributeAddFromLDAP: '
120
                    . 'skipping because ' . $attribute . ' is present'
121
                );
122
                return;
123
            }
124
        }
125
126
        // perform a merge on the ldap_search_filter
127
        // loop over the attributes and build the search and replace arrays
128
        $arrSearch = [];
129
        $arrReplace = [];
130
        foreach ($attributes as $attr => $val) {
131
            $arrSearch[] = '%' . $attr . '%';
132
133
            if (strlen($val[0]) > 0) {
134
                $arrReplace[] = Ldap::escapeFilterValue($val[0]);
135
            } else {
136
                $arrReplace[] = '';
137
            }
138
        }
139
140
        // merge the attributes into the ldap_search_filter
141
        $filter = str_replace($arrSearch, $arrReplace, $this->search_filter);
0 ignored issues
show
Bug introduced by
It seems like $arrReplace can also be of type array<mixed,array|string>; however, parameter $replace of str_replace() does only seem to accept string|string[], 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

141
        $filter = str_replace($arrSearch, /** @scrutinizer ignore-type */ $arrReplace, $this->search_filter);
Loading history...
142
143
        if (strpos($filter, '%') !== false) {
144
            Logger::info(
145
                'AttributeAddFromLDAP: There are non-existing attributes in the search filter. (' .
146
                $this->search_filter . ')'
147
            );
148
            return;
149
        }
150
151
        if (!in_array($this->attr_policy, ['merge', 'replace', 'add'], true)) {
152
            Logger::warning("AttributeAddFromLDAP: 'attribute.policy' must be one of 'merge'," .
153
                "'replace' or 'add'.");
154
            return;
155
        }
156
157
        // getLdap
158
        try {
159
            $ldap = $this->getLdap();
160
        } catch (Exception $e) {
161
            // Added this warning in case $this->getLdap() fails
162
            Logger::warning("AttributeAddFromLDAP: exception = " . $e);
163
            return;
164
        }
165
        // search for matching entries
166
        try {
167
            $entries = $ldap->searchformultiple(
168
                $this->base_dn,
169
                $filter,
170
                array_values($this->search_attributes),
171
                $this->binary_attributes,
172
                true,
173
                false
174
            );
175
        } catch (Exception $e) {
176
            return; // silent fail, error is still logged by LDAP search
177
        }
178
179
        // handle [multiple] values
180
        foreach ($entries as $entry) {
181
            foreach ($this->search_attributes as $target => $name) {
182
                if (is_numeric($target)) {
183
                    $target = $name;
184
                }
185
186
                if (isset($attributes[$target]) && $this->attr_policy === 'replace') {
187
                    unset($attributes[$target]);
188
                }
189
                $name = strtolower($name);
190
                if (isset($entry[$name])) {
191
                    unset($entry[$name]['count']);
192
                    if (isset($attributes[$target])) {
193
                        foreach (array_values($entry[$name]) as $value) {
194
                            if ($this->attr_policy === 'merge') {
195
                                if (!in_array($value, $attributes[$target], true)) {
196
                                    $attributes[$target][] = $value;
197
                                }
198
                            } else {
199
                                $attributes[$target][] = $value;
200
                            }
201
                        }
202
                    } else {
203
                        $attributes[$target] = array_values($entry[$name]);
204
                    }
205
                }
206
            }
207
        }
208
    }
209
}
210