Passed
Pull Request — master (#28)
by Tim
02:07
created

AttributeAddFromLDAP   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 18
eloc 71
c 2
b 0
f 0
dl 0
loc 157
rs 10
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 Exception;
14
use SimpleSAML\Assert\Assert;
15
use SimpleSAML\Logger;
16
use SimpleSAML\Module\ldap\Utils\Ldap as LdapUtils;
17
use Symfony\Component\Ldap\Adapter\ExtLdap\Query;
18
19
class AttributeAddFromLDAP extends BaseFilter
20
{
21
    /**
22
     * LDAP attributes to add to the request attributes
23
     *
24
     * @var array
25
     */
26
    protected array $searchAttributes;
27
28
    /**
29
     * LDAP attributes to base64 encode
30
     *
31
     * @var array
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 */
50
    protected string $searchUsername;
51
52
    /** @var string */
53
    protected string $searchPassword;
54
55
    /**
56
     * Initialize this filter.
57
     *
58
     * @param array $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->getArray('attributes.binary', []);
67
        $this->searchAttributes = $this->config->getArrayize('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->getString('attribute.policy', 'merge');
76
        Assert::oneOf($this->attrPolicy, ['merge', 'replace', 'add']);
77
78
        $this->searchUsername = $this->config->getString('search.username');
79
        $this->searchPassword = $this->config->getString('search.password', null);
80
    }
81
82
83
    /**
84
     * Add attributes from an LDAP server.
85
     *
86
     * @param array &$state The current request
87
     */
88
    public function process(array &$state): void
89
    {
90
        Assert::keyExists($state, 'Attributes');
91
        $attributes = &$state['Attributes'];
92
93
        $ldapUtils = new LdapUtils();
94
95
        // perform a merge on the ldap_search_filter
96
        // loop over the attributes and build the search and replace arrays
97
        $arrSearch = $arrReplace = [];
98
        foreach ($attributes as $attr => $val) {
99
            $arrSearch[] = '%' . $attr . '%';
100
101
            if (strlen($val[0]) > 0) {
102
                $arrReplace[] = $ldapUtils->escapeFilterValue($val[0]);
103
            } else {
104
                $arrReplace[] = '';
105
            }
106
        }
107
108
        // merge the attributes into the ldap_search_filter
109
        $filter = str_replace($arrSearch, $arrReplace, $this->searchFilter);
110
        if (strpos($filter, '%') !== false) {
111
            Logger::info(sprintf(
112
                '%s: AttributeAddFromLDAP: There are non-existing attributes in the search filter. (%s)'
113
                $this->title,
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_VARIABLE, expecting ',' or ')' on line 113 at column 16
Loading history...
114
                $filter
115
            ));
116
            return;
117
        }
118
119
        $ldap = $ldapUtils->bind($this->ldapServers, $this->searchUsername, $this->searchPassword);
120
121
        $options = [
122
            'scope' => $this->config->getString('search.scope', QUERY::SCOPE_SUB),
123
            'timeout' => $this->config->getInteger('timeout', 3),
124
        ];
125
126
        $entries = $ldapUtils->searchForMultiple(
127
            $ldap,
128
            $this->searchBase,
129
            $filter,
130
            $options,
131
            true
132
        );
133
134
        $results = [];
135
        foreach ($entries as $entry) {
136
            $tmp = array_intersect_key(
137
                $entry->getAttributes(),
138
                array_fill_keys(array_values($this->searchAttributes), null)
139
            );
140
141
            $binaries = array_intersect(
142
                array_keys($tmp),
143
                $this->binaryAttributes,
144
            );
145
            foreach ($binaries as $binary) {
146
                $tmp[$binary] = array_map('base64_encode', $entry->getAttribute($binary));
147
            }
148
149
            $results[] = $tmp;
150
        }
151
152
        // handle [multiple] values
153
        foreach ($results as $entry) {
154
            foreach ($this->searchAttributes as $target => $name) {
155
                // If there is no mapping defined, just use the name of the LDAP-attribute as a target
156
                if (is_int($target)) {
157
                    $target = $name;
158
                }
159
160
                if (isset($attributes[$target]) && $this->attrPolicy === 'replace') {
161
                    unset($attributes[$target]);
162
                }
163
164
                if (isset($entry[$name])) {
165
                    if (isset($attributes[$target])) {
166
                        foreach (array_values($entry[$name]) as $value) {
167
                            if ($this->attrPolicy === 'merge') {
168
                                if (!in_array($value, $attributes[$target], true)) {
169
                                    $attributes[$target][] = $value;
170
                                }
171
                            } else {
172
                                $attributes[$target][] = $value;
173
                            }
174
                        }
175
                    } else {
176
                        $attributes[$target] = array_values($entry[$name]);
177
                    }
178
                }
179
            }
180
        }
181
    }
182
}
183