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