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

Ldap::search()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 29
c 1
b 0
f 0
dl 0
loc 49
rs 8.5226
cc 7
nc 12
nop 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\ldap\Auth\Source;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\Configuration;
9
use SimpleSAML\Module\core\Auth\UserPassBase;
10
use SimpleSAML\Module\ldap\Utils;
11
use Symfony\Component\Ldap\Adapter\ExtLdap\Query;
12
13
use function array_fill_keys;
14
use function array_keys;
15
use function array_map;
16
use function array_values;
17
use function explode;
18
use function sprintf;
19
use function str_replace;
20
use function var_export;
21
22
/**
23
 * LDAP authentication source.
24
 *
25
 * See the ldap-entry in config-templates/authsources.php for information about
26
 * configuration of this authentication source.
27
 *
28
 * @package simplesamlphp/simplesamlphp-module-ldap
29
 */
30
31
class Ldap extends UserPassBase
32
{
33
    /**
34
     * An LDAP configuration object.
35
     */
36
    private Configuration $ldapConfig;
37
38
39
    /**
40
     * Constructor for this authentication source.
41
     *
42
     * @param array $info  Information about this authentication source.
43
     * @param array $config  Configuration.
44
     */
45
    public function __construct(array $info, array $config)
46
    {
47
        // Call the parent constructor first, as required by the interface
48
        parent::__construct($info, $config);
49
50
        $this->ldapConfig = Configuration::loadFromArray(
51
            $config,
52
            'authsources[' . var_export($this->authId, true) . ']'
53
        );
54
    }
55
56
57
    /**
58
     * Attempt to log in using the given username and password.
59
     *
60
     * @param string $username  The username the user wrote.
61
     * @param string $password  The password the user wrote.
62
     * @return array  Associative array with the users attributes.
63
     */
64
    protected function login(string $username, string $password): array
65
    {
66
        $encryption = $this->ldapConfig->getString('encryption', 'ssl');
67
        Assert::oneOf($encryption, ['none', 'ssl', 'tls']);
68
69
        $version = $this->ldapConfig->getInteger('version', 3);
70
        Assert::positiveInteger($version);
71
72
        $ldapUtils = new Utils\Ldap();
73
        $ldapServers = $ldapUtils->create(
74
            explode(' ', $this->ldapConfig->getString('connection_string')),
75
            $encryption,
76
            $version,
77
            $this->ldapConfig->getString('extension', 'ext_ldap')
78
        );
79
80
        $searchScope = $this->ldapConfig->getString('search.scope', Query::SCOPE_SUB);
81
        Assert::oneOf($searchScope, [Query::SCOPE_BASE, Query::SCOPE_ONE, Query::SCOPE_SUB]);
82
83
        $referrals = $this->ldapConfig->getValue('referrals', Query::DEREF_NEVER);
84
        Assert::oneOf($referrals, [Query::DEREF_ALWAYS, Query::DEREF_NEVER, Query::DEREF_FINDING, Query::DEREF_SEARCHING]);
85
86
        $timeout = $this->ldapConfig->getString('timeout', 3);
87
        $searchBase = $this->ldapConfig->getArray('search.base');
88
        $options = [
89
            'scope' => $searchScope,
90
            'timeout' => $timeout,
91
            'deref' => $referrals,
92
        ];
93
94
        $searchEnable = $this->ldapConfig->getBoolean('search.enable', false);
95
        if ($searchEnable === false) {
96
            $dnPattern = $this->ldapConfig->getString('dnpattern');
97
            $dn = str_replace('%username%', $username, $dnPattern);
98
        } else {
99
            $searchUsername = $this->ldapConfig->getString('search.username');
100
            Assert::notWhitespaceOnly($searchUsername);
101
102
            $searchPassword = $this->ldapConfig->getString('search.password', null);
103
            Assert::nullOrnotWhitespaceOnly($searchPassword);
104
105
            $searchAttributes = $this->ldapConfig->getArray('search.attributes');
106
            $searchFilter = $this->ldapConfig->getString('search.filter', null);
107
108
            $ldap = $ldapUtils->bind($ldapServers, $searchUsername, $searchPassword);
109
110
            $filter = '';
111
            foreach ($searchAttributes as $attr) {
112
                $filter .= '(' . $attr . '=' . $username . ')';
113
            }
114
            $filter = '(|' . $filter . ')';
115
116
            // Append LDAP filters if defined
117
            if ($searchFilter !== null) {
118
                $filter = "(&" . $filter . $searchFilter . ")";
119
            }
120
121
            /** @psalm-var \Symfony\Component\Ldap\Entry $entry */
122
            $entry = $ldapUtils->search($ldap, $searchBase, $filter, $options, false);
123
            $dn = $entry->getDn();
124
        }
125
126
        $ldap = $ldapUtils->bind($ldapServers, $dn, $password);
127
        $filter = sprintf('(distinguishedName=%s)', $dn);
128
129
        /** @psalm-var \Symfony\Component\Ldap\Entry $entry */
130
        $entry = $ldapUtils->search($ldap, $searchBase, $filter, $options, false);
131
132
        $attributes = $this->ldapConfig->getValue('attributes', []);
133
        if ($attributes === null) {
134
            $result = $entry->getAttributes();
135
        } else {
136
            Assert::isArray($attributes);
137
            $result = array_intersect_key(
138
                $entry->getAttributes(),
139
                array_fill_keys(array_values($attributes), null)
140
            );
141
        }
142
143
        $binaries = array_intersect(
144
            array_keys($result),
145
            $this->ldapConfig->getArray('attributes.binary', []),
146
        );
147
        foreach ($binaries as $binary) {
148
            $result[$binary] = array_map('base64_encode', $result[$binary]);
149
        }
150
151
        return $result;
152
    }
153
}
154