BaseFilter::parseAuthSourceConfig()   F
last analyzed

Complexity

Conditions 23
Paths 10562

Size

Total Lines 91
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 49
dl 0
loc 91
rs 0
c 0
b 0
f 0
cc 23
nc 10562
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
 * This base LDAP filter class can be extended to enable real filter classes direct access
5
 * access to the authsource ldap config and connects to the ldap server.
6
 *
7
 * @package simplesamlphp/simplesamlphp-module-ldap
8
 */
9
10
declare(strict_types=1);
11
12
namespace SimpleSAML\Module\ldap\Auth\Process;
13
14
use SimpleSAML\Auth;
15
use SimpleSAML\Configuration;
16
use SimpleSAML\Error;
17
use SimpleSAML\Logger;
18
use SimpleSAML\Module\ldap\ConnectorFactory;
19
use SimpleSAML\Module\ldap\ConnectorInterface;
20
21
abstract class BaseFilter extends Auth\ProcessingFilter
22
{
23
    /**
24
     * TODO: Support ldap:LDAPMulti, if possible
25
     *
26
     * @var string[]
27
     */
28
    protected static array $ldapsources = ['ldap:Ldap', 'authX509:X509userCert'];
29
30
    /**
31
     * List of attribute "alias's" linked to the real attribute
32
     * name. Used for abstraction / configuration of the LDAP
33
     * attribute names, which may change between dir service.
34
     *
35
     * @var array<mixed>
36
     */
37
    protected array $attribute_map;
38
39
    /**
40
     * The base DN of the LDAP connection. Used when searching the LDAP server.
41
     *
42
     * @var array<mixed>
43
     */
44
    protected array $searchBase;
45
46
    /**
47
     * The construct method will change the filter config into
48
     * a \SimpleSAML\Configuration object and store it here for
49
     * later use, if needed.
50
     *
51
     * @var \SimpleSAML\Configuration
52
     */
53
    protected Configuration $config;
54
55
    /**
56
     * Array of LDAP connection objects. Stored here to be accessed later during processing.
57
     *
58
     * @var \SimpleSAML\Module\ldap\ConnectorInterface
59
     */
60
    protected ConnectorInterface $connector;
61
62
    /**
63
     * The class "title" used in logging and exception messages.
64
     * This should be prepended to the beginning of the message.
65
     *
66
     * @var string
67
     */
68
    protected string $title = 'ldap:BaseFilter';
69
70
    /**
71
     * List of LDAP object types, used to determine the type of
72
     * object that a DN references.
73
     *
74
     * @var array<mixed>
75
     */
76
    protected array $type_map;
77
78
79
    /**
80
     * Checks the authsource, if defined, for configuration values
81
     * to the LDAP server. Then sets up the LDAP connection for the
82
     * instance/object and stores everything in class members.
83
     *
84
     * @throws \SimpleSAML\Error\Exception
85
     * @param array<mixed> &$config
86
     * @param mixed $reserved
87
     */
88
    public function __construct(array &$config, $reserved)
89
    {
90
        parent::__construct($config, $reserved);
91
92
        // Change the class $title to match it's true name
93
        // This way if the class is extended the proper name is used
94
        $classname = explode('_', get_class($this));
95
        $classname = end($classname);
96
        $this->title = 'ldap:' . $classname;
97
98
        // Log the construction
99
        Logger::debug(sprintf('%s : Creating and configuring the filter.', $this->title));
100
101
        // If an authsource was defined (an not empty string)...
102
        if (isset($config['authsource']) && $config['authsource'] !== '') {
103
            $authconfig = $this->parseAuthSourceConfig($config['authsource']);
104
105
            // Merge the authsource config with the filter config,
106
            // but have the filter config override the authsource config
107
            $config = array_merge($authconfig, $config);
108
109
            // Authsource complete
110
            Logger::debug(sprintf(
111
                '%s : Retrieved authsource [%s] configuration values: %s',
112
                $this->title,
113
                $config['authsource'],
114
                $this->varExport($authconfig),
115
            ));
116
        }
117
118
        // Convert the config array to a config class,
119
        // that way we can verify type and define defaults.
120
        // Store in the instance in-case needed later, by a child class.
121
        $this->config = Configuration::loadFromArray($config, 'ldap:AuthProcess');
122
123
        // Initialize the Ldap-object
124
        $this->connector = ConnectorFactory::fromAuthSource($config['authsource']);
125
126
        // Set all the filter values, setting defaults if needed
127
        $this->searchBase = $this->config->getOptionalArray('search.base', []);
128
129
        // Log the member values retrieved above
130
        Logger::debug(sprintf(
131
            '%s : Configuration values retrieved; BaseDN: %s',
132
            $this->title,
133
            $this->varExport($this->searchBase),
134
        ));
135
136
        // Setup the attribute map which will be used to search LDAP
137
        $this->attribute_map = [
138
            'dn'       => $this->config->getOptionalString('attribute.dn', 'distinguishedName'),
139
            'groups'   => $this->config->getOptionalString('attribute.groups', 'groups'),
140
            'member'   => $this->config->getOptionalString('attribute.member', 'member'),
141
            'memberOf' => $this->config->getOptionalString('attribute.memberOf', 'memberOf'),
142
            'name'     => $this->config->getOptionalString('attribute.groupname', 'name'),
143
            'return'   => $this->config->getOptionalString('attribute.return', 'distinguishedName'),
144
            'type'     => $this->config->getOptionalString('attribute.type', 'objectClass'),
145
            'username' => $this->config->getOptionalString('attribute.username', 'sAMAccountName'),
146
        ];
147
148
        // Log the attribute map
149
        Logger::debug(sprintf(
150
            '%s : Attribute map created: %s',
151
            $this->title,
152
            $this->varExport($this->attribute_map),
153
        ));
154
155
        // Setup the object type map which is used to determine a DNs' type
156
        $this->type_map = [
157
            'group' => $this->config->getOptionalString('type.group', 'group'),
158
            'user'  => $this->config->getOptionalString('type.user', 'user'),
159
        ];
160
161
        // Log the type map
162
        Logger::debug(sprintf(
163
            '%s : Type map created: %s',
164
            $this->title,
165
            $this->varExport($this->type_map),
166
        ));
167
    }
168
169
170
    /**
171
     * Parse authsource config
172
     *
173
     * @param string $as The name of the authsource
174
     * @return array<mixed>
175
     */
176
    private function parseAuthSourceConfig(string $as): array
177
    {
178
        // Log the authsource request
179
        Logger::debug(sprintf(
180
            '%s : Attempting to get configuration values from authsource [%s]',
181
            $this->title,
182
            $as,
183
        ));
184
185
        // Get the authsources file, which should contain the config
186
        $authsources = Configuration::getConfig('authsources.php');
187
188
        // Verify that the authsource config exists
189
        if (!$authsources->hasValue($as)) {
190
            throw new Error\Exception(sprintf(
191
                '%s : Authsource [%s] defined in filter parameters not found in authsources.php',
192
                $this->title,
193
                $as,
194
            ));
195
        }
196
197
        // Get just the specified authsource config values
198
        $authsource = $authsources->getArray($as);
199
200
        // Make sure it is an ldap source
201
        if (isset($authsource[0]) && !in_array($authsource[0], self::$ldapsources)) {
202
            throw new Error\Exception(sprintf(
203
                '%s : Authsource [%s] specified in filter parameters is not an ldap:LDAP type',
204
                $this->title,
205
                $as,
206
            ));
207
        }
208
209
        // Build the authsource config
210
        $authconfig = [];
211
        if (isset($authsource['connection_string'])) {
212
            $authconfig['connection_string'] = $authsource['connection_string'];
213
        }
214
        if (isset($authsource['encryption'])) {
215
            $authconfig['encryption'] = $authsource['encryption'];
216
        }
217
        if (isset($authsource['version'])) {
218
            $authconfig['version'] = $authsource['version'];
219
        }
220
        if (isset($authsource['timeout'])) {
221
            $authconfig['timeout'] = $authsource['timeout'];
222
        }
223
        if (isset($authsource['debug'])) {
224
            $authconfig['debug']      = $authsource['debug'];
225
        }
226
        if (isset($authsource['referrals'])) {
227
            $authconfig['referrals']  = $authsource['referrals'];
228
        }
229
230
        // only set when search.enabled = true
231
        if (isset($authsource['search.enable']) && ($authsource['search.enable'] === true)) {
232
            if (isset($authsource['search.base'])) {
233
                $authconfig['search.base'] = $authsource['search.base'];
234
            }
235
            if (isset($authsource['search.scope'])) {
236
                $authconfig['search.scope'] = $authsource['search.scope'];
237
            }
238
239
            if (isset($authsource['search.username'])) {
240
                $authconfig['search.username']   = $authsource['search.username'];
241
            }
242
            if (isset($authsource['search.password'])) {
243
                $authconfig['search.password']   = $authsource['search.password'];
244
            }
245
246
            // Only set the username attribute if the authsource specifies one attribute
247
            if (
248
                isset($authsource['search.attributes'])
249
                && is_array($authsource['search.attributes'])
250
                && count($authsource['search.attributes']) == 1
251
            ) {
252
                $authconfig['attribute.username'] = reset($authsource['search.attributes']);
253
            }
254
        }
255
256
        // only set when priv.read = true
257
        if (isset($authsource['priv.read']) && $authsource['priv.read']) {
258
            if (isset($authsource['priv.username'])) {
259
                $authconfig['priv.username'] = $authsource['priv.username'];
260
            }
261
            if (isset($authsource['priv.password'])) {
262
                $authconfig['priv.password'] = $authsource['priv.password'];
263
            }
264
        }
265
266
        return $authconfig;
267
    }
268
269
270
    /**
271
     * Local utility function to get details about a variable,
272
     * basically converting it to a string to be used in a log
273
     * message. The var_export() function returns several lines
274
     * so this will remove the new lines and trim each line.
275
     *
276
     * @param mixed $value
277
     * @return string
278
     */
279
    protected function varExport($value): string
280
    {
281
        if (is_array($value)) {
282
            // remove sensitive data
283
            foreach ($value as $key => &$val) {
284
                if ($key === 'search.password' || $key === 'priv.password') {
285
                    $val = empty($val) ? '' : '********';
286
                }
287
            }
288
            unset($val);
289
        }
290
291
        $export = var_export($value, true);
292
        $lines = explode("\n", $export);
293
        foreach ($lines as &$line) {
294
            $line = trim($line);
295
        }
296
        return implode(' ', $lines);
297
    }
298
}
299