Passed
Pull Request — master (#32)
by Tim
03:25
created

BaseFilter::parseAuthSourceConfig()   F

Complexity

Conditions 23
Paths 10562

Size

Total Lines 90
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

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