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

BaseFilter::parseAuthSourceConfig()   F

Complexity

Conditions 23
Paths 10562

Size

Total Lines 84
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 23
eloc 43
c 1
b 0
f 0
nc 10562
nop 1
dl 0
loc 84
rs 0

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