Passed
Pull Request — master (#28)
by Tim
32:02 queued 29:57
created

BaseFilter::getLdap()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 29
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 20
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 29
rs 9.2888
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', '');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->config->getArrayi...ring('search.base', '') can also be of type string. However, the property $searchBase is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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,
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