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