xref: /plugin/pureldap/classes/Client.php (revision 5a3b912293b77009ff72b70b480eb77929df5103)
11078ec26SAndreas Gohr<?php
21078ec26SAndreas Gohr
31078ec26SAndreas Gohrnamespace dokuwiki\plugin\pureldap\classes;
41078ec26SAndreas Gohr
51078ec26SAndreas Gohruse FreeDSx\Ldap\Entry\Attribute;
61078ec26SAndreas Gohruse FreeDSx\Ldap\Exception\BindException;
71078ec26SAndreas Gohruse FreeDSx\Ldap\Exception\ConnectionException;
81078ec26SAndreas Gohruse FreeDSx\Ldap\Exception\OperationException;
91078ec26SAndreas Gohruse FreeDSx\Ldap\LdapClient;
101078ec26SAndreas Gohr
111078ec26SAndreas Gohrrequire_once __DIR__ . '/../vendor/autoload.php';
121078ec26SAndreas Gohr
131078ec26SAndreas Gohrabstract class Client
141078ec26SAndreas Gohr{
151078ec26SAndreas Gohr    /** @var array the configuration */
161078ec26SAndreas Gohr    protected $config;
171078ec26SAndreas Gohr
181078ec26SAndreas Gohr    /** @var LdapClient */
191078ec26SAndreas Gohr    protected $ldap;
201078ec26SAndreas Gohr
211078ec26SAndreas Gohr    /** @var bool is this client authenticated already? */
221078ec26SAndreas Gohr    protected $isAuthenticated = false;
231078ec26SAndreas Gohr
241078ec26SAndreas Gohr    /** @var array cached user info */
251078ec26SAndreas Gohr    protected $userCache = [];
261078ec26SAndreas Gohr
27*5a3b9122SAndreas Gohr    /** @var array cached group list */
28*5a3b9122SAndreas Gohr    protected $groupCache = [];
29*5a3b9122SAndreas Gohr
301078ec26SAndreas Gohr    /**
311078ec26SAndreas Gohr     * Client constructor.
321078ec26SAndreas Gohr     * @param array $config
331078ec26SAndreas Gohr     */
341078ec26SAndreas Gohr    public function __construct($config)
351078ec26SAndreas Gohr    {
361078ec26SAndreas Gohr        $this->config = $this->prepareConfig($config);
371078ec26SAndreas Gohr        $this->ldap = new LdapClient($this->config);
381078ec26SAndreas Gohr    }
391078ec26SAndreas Gohr
401078ec26SAndreas Gohr    /**
411078ec26SAndreas Gohr     * Setup sane config defaults
421078ec26SAndreas Gohr     *
431078ec26SAndreas Gohr     * @param array $config
441078ec26SAndreas Gohr     * @return array
451078ec26SAndreas Gohr     */
461078ec26SAndreas Gohr    protected function prepareConfig($config)
471078ec26SAndreas Gohr    {
481078ec26SAndreas Gohr        $defaults = [
491078ec26SAndreas Gohr            'defaultgroup' => 'user', // we expect this to be passed from global conf
501078ec26SAndreas Gohr            'use_tls' => false,
511078ec26SAndreas Gohr            'use_ssl' => false,
521078ec26SAndreas Gohr            'port' => '',
531078ec26SAndreas Gohr            'admin_username' => '',
541078ec26SAndreas Gohr            'admin_password' => '',
55*5a3b9122SAndreas Gohr            'page_size' => 1000,
561078ec26SAndreas Gohr        ];
571078ec26SAndreas Gohr
581078ec26SAndreas Gohr        $config = array_merge($defaults, $config);
591078ec26SAndreas Gohr
601078ec26SAndreas Gohr        // default port depends on SSL setting
611078ec26SAndreas Gohr        if (!$config['port']) {
621078ec26SAndreas Gohr            $config['port'] = $config['use_ssl'] ? 636 : 389;
631078ec26SAndreas Gohr        }
641078ec26SAndreas Gohr
651078ec26SAndreas Gohr        return $config;
661078ec26SAndreas Gohr    }
671078ec26SAndreas Gohr
681078ec26SAndreas Gohr    /**
691078ec26SAndreas Gohr     * Authenticate as admin
701078ec26SAndreas Gohr     */
711078ec26SAndreas Gohr    public function autoAuth()
721078ec26SAndreas Gohr    {
731078ec26SAndreas Gohr        if ($this->isAuthenticated) return true;
741078ec26SAndreas Gohr        return $this->authenticate($this->config['admin_username'], $this->config['admin_password']);
751078ec26SAndreas Gohr    }
761078ec26SAndreas Gohr
771078ec26SAndreas Gohr    /**
781078ec26SAndreas Gohr     * Authenticates a given user. This client will remain authenticated
791078ec26SAndreas Gohr     *
801078ec26SAndreas Gohr     * @param string $user
811078ec26SAndreas Gohr     * @param string $pass
821078ec26SAndreas Gohr     * @return bool was the authentication successful?
83*5a3b9122SAndreas Gohr     * @noinspection PhpRedundantCatchClauseInspection
841078ec26SAndreas Gohr     */
851078ec26SAndreas Gohr    public function authenticate($user, $pass)
861078ec26SAndreas Gohr    {
871078ec26SAndreas Gohr        if ($this->config['use_tls']) {
881078ec26SAndreas Gohr            try {
891078ec26SAndreas Gohr                $this->ldap->startTls();
901078ec26SAndreas Gohr            } catch (OperationException $e) {
911078ec26SAndreas Gohr                $this->debug($e);
921078ec26SAndreas Gohr            }
931078ec26SAndreas Gohr        }
941078ec26SAndreas Gohr
951078ec26SAndreas Gohr        try {
961078ec26SAndreas Gohr            $this->ldap->bind($user, $pass);
971078ec26SAndreas Gohr        } catch (BindException $e) {
981078ec26SAndreas Gohr            return false;
991078ec26SAndreas Gohr        } catch (ConnectionException $e) {
1001078ec26SAndreas Gohr            $this->debug($e);
1011078ec26SAndreas Gohr            return false;
1021078ec26SAndreas Gohr        } catch (OperationException $e) {
1031078ec26SAndreas Gohr            $this->debug($e);
1041078ec26SAndreas Gohr            return false;
1051078ec26SAndreas Gohr        }
1061078ec26SAndreas Gohr
1071078ec26SAndreas Gohr        $this->isAuthenticated = true;
1081078ec26SAndreas Gohr        return true;
1091078ec26SAndreas Gohr    }
1101078ec26SAndreas Gohr
1111078ec26SAndreas Gohr    /**
1121078ec26SAndreas Gohr     * Get info for a single user, use cache if available
1131078ec26SAndreas Gohr     *
1141078ec26SAndreas Gohr     * @param string $username
1151078ec26SAndreas Gohr     * @param bool $fetchgroups Are groups needed?
1161078ec26SAndreas Gohr     * @return array|null
1171078ec26SAndreas Gohr     */
1181078ec26SAndreas Gohr    public function getCachedUser($username, $fetchgroups = true)
1191078ec26SAndreas Gohr    {
1201078ec26SAndreas Gohr        if (isset($this->userCache[$username])) {
1211078ec26SAndreas Gohr            if (!$fetchgroups || is_array($this->userCache[$username]['grps'])) {
1221078ec26SAndreas Gohr                return $this->userCache[$username];
1231078ec26SAndreas Gohr            }
1241078ec26SAndreas Gohr        }
1251078ec26SAndreas Gohr
1261078ec26SAndreas Gohr        // fetch fresh data
1271078ec26SAndreas Gohr        $info = $this->getUser($username, $fetchgroups);
1281078ec26SAndreas Gohr
1291078ec26SAndreas Gohr        // store in cache
1301078ec26SAndreas Gohr        if ($info !== null) {
1311078ec26SAndreas Gohr            $this->userCache[$username] = $info;
1321078ec26SAndreas Gohr        }
1331078ec26SAndreas Gohr
1341078ec26SAndreas Gohr        return $info;
1351078ec26SAndreas Gohr    }
1361078ec26SAndreas Gohr
1371078ec26SAndreas Gohr    /**
1381078ec26SAndreas Gohr     * Fetch a single user
1391078ec26SAndreas Gohr     *
1401078ec26SAndreas Gohr     * @param string $username
1411078ec26SAndreas Gohr     * @param bool $fetchgroups Shall groups be fetched, too?
1421078ec26SAndreas Gohr     * @return null|array
1431078ec26SAndreas Gohr     */
1441078ec26SAndreas Gohr    abstract public function getUser($username, $fetchgroups = true);
1451078ec26SAndreas Gohr
1461078ec26SAndreas Gohr    /**
147*5a3b9122SAndreas Gohr     * Return a list of all available groups, use cache if available
148*5a3b9122SAndreas Gohr     *
149*5a3b9122SAndreas Gohr     * @return string[]
150*5a3b9122SAndreas Gohr     */
151*5a3b9122SAndreas Gohr    public function getCachedGroups()
152*5a3b9122SAndreas Gohr    {
153*5a3b9122SAndreas Gohr        if (empty($this->groupCache)) {
154*5a3b9122SAndreas Gohr            $this->groupCache = $this->getGroups();
155*5a3b9122SAndreas Gohr        }
156*5a3b9122SAndreas Gohr
157*5a3b9122SAndreas Gohr        return $this->groupCache;
158*5a3b9122SAndreas Gohr    }
159*5a3b9122SAndreas Gohr
160*5a3b9122SAndreas Gohr    /**
161*5a3b9122SAndreas Gohr     * Return a list of all available groups
162*5a3b9122SAndreas Gohr     *
163*5a3b9122SAndreas Gohr     * @return string[]
164*5a3b9122SAndreas Gohr     */
165*5a3b9122SAndreas Gohr    abstract public function getGroups();
166*5a3b9122SAndreas Gohr
167*5a3b9122SAndreas Gohr    /**
1681078ec26SAndreas Gohr     * Helper method to get the first value of the given attribute
1691078ec26SAndreas Gohr     *
1701078ec26SAndreas Gohr     * The given attribute may be null, an empty string is returned then
1711078ec26SAndreas Gohr     *
1721078ec26SAndreas Gohr     * @param Attribute|null $attribute
1731078ec26SAndreas Gohr     * @return string
1741078ec26SAndreas Gohr     */
175*5a3b9122SAndreas Gohr    protected function attr2str($attribute)
176*5a3b9122SAndreas Gohr    {
1771078ec26SAndreas Gohr        if ($attribute !== null) {
1781078ec26SAndreas Gohr            return $attribute->firstValue();
1791078ec26SAndreas Gohr        }
1801078ec26SAndreas Gohr        return '';
1811078ec26SAndreas Gohr    }
1821078ec26SAndreas Gohr
1831078ec26SAndreas Gohr    /**
1841078ec26SAndreas Gohr     * Handle debugging
1851078ec26SAndreas Gohr     *
1861078ec26SAndreas Gohr     * @param \Exception $e
1871078ec26SAndreas Gohr     */
1881078ec26SAndreas Gohr    protected function debug(\Exception $e)
1891078ec26SAndreas Gohr    {
1901078ec26SAndreas Gohr        if (defined('DOKU_UNITTEST')) {
1918595f73eSAndreas Gohr            throw new \RuntimeException('', 0, $e);
1921078ec26SAndreas Gohr        }
1931078ec26SAndreas Gohr
1941078ec26SAndreas Gohr        msg($e->getMessage(), -1);
1951078ec26SAndreas Gohr    }
1961078ec26SAndreas Gohr}
197