xref: /plugin/pureldap/classes/Client.php (revision 80ac552f15be014c3406f65bcaa71e07c7c359c8)
1<?php
2
3namespace dokuwiki\plugin\pureldap\classes;
4
5use dokuwiki\Utf8\PhpString;
6use FreeDSx\Ldap\Entry\Attribute;
7use FreeDSx\Ldap\Exception\BindException;
8use FreeDSx\Ldap\Exception\ConnectionException;
9use FreeDSx\Ldap\Exception\OperationException;
10use FreeDSx\Ldap\LdapClient;
11
12require_once __DIR__ . '/../vendor/autoload.php';
13
14abstract class Client
15{
16    /** @var array the configuration */
17    protected $config;
18
19    /** @var LdapClient */
20    protected $ldap;
21
22    /** @var bool is this client authenticated already? */
23    protected $isAuthenticated = false;
24
25    /** @var array cached user info */
26    protected $userCache = [];
27
28    /** @var array cached group list */
29    protected $groupCache = [];
30
31    /**
32     * Client constructor.
33     * @param array $config
34     */
35    public function __construct($config)
36    {
37        $this->config = $this->prepareConfig($config);
38        $this->ldap = new LdapClient($this->config);
39    }
40
41    /**
42     * Setup sane config defaults
43     *
44     * @param array $config
45     * @return array
46     */
47    protected function prepareConfig($config)
48    {
49        $defaults = [
50            'defaultgroup' => 'user', // we expect this to be passed from global conf
51            'domain' => '',
52            'use_tls' => false,
53            'use_ssl' => false,
54            'port' => '',
55            'admin_username' => '',
56            'admin_password' => '',
57            'page_size' => 1000,
58        ];
59
60        $config = array_merge($defaults, $config);
61
62        // default port depends on SSL setting
63        if (!$config['port']) {
64            $config['port'] = $config['use_ssl'] ? 636 : 389;
65        }
66
67        $config['domain'] = PhpString::strtolower($config['domain']);
68
69        return $config;
70    }
71
72    /**
73     * Authenticate as admin
74     */
75    public function autoAuth()
76    {
77        if ($this->isAuthenticated) return true;
78        return $this->authenticate($this->config['admin_username'], $this->config['admin_password']);
79    }
80
81    /**
82     * Authenticates a given user. This client will remain authenticated
83     *
84     * @param string $user
85     * @param string $pass
86     * @return bool was the authentication successful?
87     * @noinspection PhpRedundantCatchClauseInspection
88     */
89    public function authenticate($user, $pass)
90    {
91        $user = $this->qualifiedUser($user);
92
93        if ($this->config['use_tls']) {
94            try {
95                $this->ldap->startTls();
96            } catch (OperationException $e) {
97                $this->fatal($e);
98            }
99        }
100
101        try {
102            $this->ldap->bind($user, $pass);
103        } catch (BindException $e) {
104            return false;
105        } catch (ConnectionException $e) {
106            $this->fatal($e);
107            return false;
108        } catch (OperationException $e) {
109            $this->fatal($e);
110            return false;
111        }
112
113        $this->isAuthenticated = true;
114        return true;
115    }
116
117    /**
118     * Get info for a single user, use cache if available
119     *
120     * @param string $username
121     * @param bool $fetchgroups Are groups needed?
122     * @return array|null
123     */
124    public function getCachedUser($username, $fetchgroups = true)
125    {
126        if (isset($this->userCache[$username])) {
127            if (!$fetchgroups || is_array($this->userCache[$username]['grps'])) {
128                return $this->userCache[$username];
129            }
130        }
131
132        // fetch fresh data
133        $info = $this->getUser($username, $fetchgroups);
134
135        // store in cache
136        if ($info !== null) {
137            $this->userCache[$username] = $info;
138        }
139
140        return $info;
141    }
142
143    /**
144     * Fetch a single user
145     *
146     * @param string $username
147     * @param bool $fetchgroups Shall groups be fetched, too?
148     * @return null|array
149     */
150    abstract public function getUser($username, $fetchgroups = true);
151
152    /**
153     * Return a list of all available groups, use cache if available
154     *
155     * @return string[]
156     */
157    public function getCachedGroups()
158    {
159        if (empty($this->groupCache)) {
160            $this->groupCache = $this->getGroups();
161        }
162
163        return $this->groupCache;
164    }
165
166    /**
167     * Return a list of all available groups
168     *
169     * Optionally filter the list
170     *
171     * @param null|string $match Filter for this, null for all groups
172     * @param string $filtermethod How to match the groups
173     * @return string[]
174     */
175    abstract public function getGroups($match = null, $filtermethod = 'equal');
176
177
178    /**
179     * Construst the fully qualified name to identify a user
180     *
181     * @param string $username
182     * @return string
183     */
184    abstract public function qualifiedUser($username);
185
186    /**
187     * Simplify the username if possible
188     *
189     * @param string $username
190     * @return string
191     */
192    abstract public function simpleUser($username);
193
194    /**
195     * Helper method to get the first value of the given attribute
196     *
197     * The given attribute may be null, an empty string is returned then
198     *
199     * @param Attribute|null $attribute
200     * @return string
201     */
202    protected function attr2str($attribute)
203    {
204        if ($attribute !== null) {
205            return $attribute->firstValue();
206        }
207        return '';
208    }
209
210    /**
211     * Handle fatal exceptions
212     *
213     * @param \Exception $e
214     */
215    protected function fatal(\Exception $e)
216    {
217        if (defined('DOKU_UNITTEST')) {
218            throw new \RuntimeException('', 0, $e);
219        }
220        msg('[pureldap] ' . hsc($e->getMessage()) . ' at ' . $e->getFile() . ':' . $e->getLine(), -1);
221    }
222
223    /**
224     * Handle debug output
225     *
226     * @param string $msg
227     * @param string $file
228     * @param int $line
229     */
230    protected function debug($msg, $file, $line)
231    {
232        msg('[pureldap] ' . hsc($msg) . ' at ' . $file . ':' . $line, 0);
233    }
234}
235