Passed
Pull Request — master (#28)
by Tim
02:18
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 Method

Rating   Name   Duplication   Size   Complexity  
A BaseFilter::varExport() 0 18 6
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($this->title . 'Creating and configuring the filter.');
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(
110
                $this->title . 'Retrieved authsource [' . $config['authsource'] .
111
                '] configuration values: ' . $this->varExport($authconfig)
112
            );
113
        }
114
115
        // Convert the config array to a config class,
116
        // that way we can verify type and define defaults.
117
        // Store in the instance in-case needed later, by a child class.
118
        $this->config = Configuration::loadFromArray($config, 'ldap:AuthProcess');
119
120
        // Initialize the Ldap-object
121
        $this->ldapServers = $this->initializeLdap();
122
123
        // Set all the filter values, setting defaults if needed
124
        $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...
125
126
        // Log the member values retrieved above
127
        Logger::debug(
128
            $this->title . 'Configuration values retrieved;' .
129
            ' BaseDN: ' . $this->varExport($this->searchBase)
130
        );
131
132
        // Setup the attribute map which will be used to search LDAP
133
        $this->attribute_map = [
134
            'dn'       => $this->config->getString('attribute.dn', 'distinguishedName'),
135
            'groups'   => $this->config->getString('attribute.groups', 'groups'),
136
            'member'   => $this->config->getString('attribute.member', 'member'),
137
            'memberof' => $this->config->getString('attribute.memberof', 'memberOf'),
138
            'name'     => $this->config->getString('attribute.groupname', 'name'),
139
            'return'   => $this->config->getString('attribute.return', 'distinguishedName'),
140
            'type'     => $this->config->getString('attribute.type', 'objectClass'),
141
            'username' => $this->config->getString('attribute.username', 'sAMAccountName')
142
        ];
143
144
        // Log the attribute map
145
        Logger::debug(
146
            $this->title . 'Attribute map created: ' . $this->varExport($this->attribute_map)
147
        );
148
149
        // Setup the object type map which is used to determine a DNs' type
150
        $this->type_map = [
151
            'group' => $this->config->getString('type.group', 'group'),
152
            'user'  => $this->config->getString('type.user', 'user')
153
        ];
154
155
        // Log the type map
156
        Logger::debug(
157
            $this->title . 'Type map created: ' . $this->varExport($this->type_map)
158
        );
159
    }
160
161
162
    /**
163
     * Parse authsource config
164
     *
165
     * @param string $as The name of the authsource
166
     */
167
    private function parseAuthSourceConfig(string $as) : array
168
    {
169
        // Log the authsource request
170
        Logger::debug(
171
            $this->title . 'Attempting to get configuration values from authsource [' . $as . ']'
172
        );
173
174
        // Get the authsources file, which should contain the config
175
        $authsources = Configuration::getConfig('authsources.php');
176
177
        // Verify that the authsource config exists
178
        if (!$authsources->hasValue($as)) {
179
            throw new Error\Exception(
180
                $this->title . 'Authsource [' . $as . '] defined in filter parameters not found in authsources.php'
181
            );
182
        }
183
184
        // Get just the specified authsource config values
185
        $authsource = $authsources->getArray($as);
186
187
        // Make sure it is an ldap source
188
        if (isset($authsource[0]) && !in_array($authsource[0], self::$ldapsources)) {
189
            throw new Error\Exception(
190
                $this->title . 'Authsource [' . $as . '] specified in filter parameters is not an ldap:LDAP type'
191
            );
192
        }
193
194
        // Build the authsource config
195
        $authconfig = [];
196
        if (isset($authsource['connection_string'])) {
197
            $authconfig['connection_string'] = $authsource['connection_string'];
198
        }
199
        if (isset($authsource['encryption'])) {
200
            $authconfig['encryption'] = $authsource['encryption'];
201
        }
202
        if (isset($authsource['version'])) {
203
            $authconfig['version'] = $authsource['version'];
204
        }
205
        if (isset($authsource['timeout'])) {
206
            $authconfig['timeout'] = $authsource['timeout'];
207
        }
208
        if (isset($authsource['debug'])) {
209
            $authconfig['debug']      = $authsource['debug'];
210
        }
211
        if (isset($authsource['referrals'])) {
212
            $authconfig['referrals']  = $authsource['referrals'];
213
        }
214
215
        // only set when search.enabled = true
216
        if (isset($authsource['search.enable']) && ($authsource['search.enable'] === true)) {
217
            if (isset($authsource['search.base'])) {
218
                $authconfig['search.base'] = $authsource['search.base'];
219
            }
220
            if (isset($authsource['search.scope'])) {
221
                $authconfig['search.scope'] = $authsource['search.scope'];
222
            }
223
            if (isset($authsource['search.username'])) {
224
                $authconfig['search.username']   = $authsource['search.username'];
225
            }
226
            if (isset($authsource['search.password'])) {
227
                $authconfig['search.password']   = $authsource['search.password'];
228
            }
229
230
            // Only set the username attribute if the authsource specifies one attribute
231
            if (
232
                isset($authsource['search.attributes'])
233
                && is_array($authsource['search.attributes'])
234
                && count($authsource['search.attributes']) == 1
235
            ) {
236
                $authconfig['attribute.username'] = reset($authsource['search.attributes']);
237
            }
238
        }
239
240
        // only set when priv.read = true
241
        if (isset($authsource['priv.read']) && $authsource['priv.read']) {
242
            if (isset($authsource['priv.username'])) {
243
                $authconfig['priv.username'] = $authsource['priv.username'];
244
            }
245
            if (isset($authsource['priv.password'])) {
246
                $authconfig['priv.password'] = $authsource['priv.password'];
247
            }
248
        }
249
250
        return $authconfig;
251
    }
252
253
254
    /**
255
     * Initialize the Ldap-object
256
     *
257
     * @return array
258
     */
259
    private function initializeLdap(): array
260
    {
261
        $ldapUtils = new Utils\Ldap();
262
263
        return $ldapUtils->create(
264
            explode(' ', $this->config->getString('connection_string')),
265
            $this->config->getString('encryption', 'ssl'),
266
            $this->config->getInteger('version', 3),
267
            $this->config->getString('extension', 'ext_ldap'),
268
            $this->config->getBoolean('debug', false),
269
            [
270
                'network_timeout' => $this->config->getInteger('timeout', 3),
271
                'referrals' => $this->config->getBoolean('referrals', false),
272
            ]
273
        );
274
    }
275
276
277
    /**
278
     * Local utility function to get details about a variable,
279
     * basically converting it to a string to be used in a log
280
     * message. The var_export() function returns several lines
281
     * so this will remove the new lines and trim each line.
282
     *
283
     * @param mixed $value
284
     * @return string
285
     */
286
    protected function varExport($value): string
287
    {
288
        if (is_array($value)) {
289
            // remove sensitive data
290
            foreach ($value as $key => &$val) {
291
                if ($key === 'search.password') {
292
                    $val = empty($val) ? '' : '********';
293
                }
294
            }
295
            unset($val);
296
        }
297
298
        $export = var_export($value, true);
299
        $lines = explode("\n", $export);
300
        foreach ($lines as &$line) {
301
            $line = trim($line);
302
        }
303
        return implode(' ', $lines);
304
    }
305
}
306