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