PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/plugins/authpgsql/auth.php

https://gitlab.com/michield/dokuwiki
PHP | 418 lines | 232 code | 32 blank | 154 comment | 36 complexity | 2d7976590a083f38955bf0c5f91e017d MD5 | raw file
  1. <?php
  2. // must be run within Dokuwiki
  3. if(!defined('DOKU_INC')) die();
  4. /**
  5. * PostgreSQL authentication backend
  6. *
  7. * This class inherits much functionality from the MySQL class
  8. * and just reimplements the Postgres specific parts.
  9. *
  10. * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
  11. * @author Andreas Gohr <andi@splitbrain.org>
  12. * @author Chris Smith <chris@jalakai.co.uk>
  13. * @author Matthias Grimm <matthias.grimmm@sourceforge.net>
  14. * @author Jan Schumann <js@schumann-it.com>
  15. */
  16. class auth_plugin_authpgsql extends auth_plugin_authmysql {
  17. /**
  18. * Constructor
  19. *
  20. * checks if the pgsql interface is available, otherwise it will
  21. * set the variable $success of the basis class to false
  22. *
  23. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  24. * @author Andreas Gohr <andi@splitbrain.org>
  25. */
  26. public function __construct() {
  27. // we don't want the stuff the MySQL constructor does, but the grandparent might do something
  28. DokuWiki_Auth_Plugin::__construct();
  29. if(!function_exists('pg_connect')) {
  30. $this->_debug("PgSQL err: PHP Postgres extension not found.", -1, __LINE__, __FILE__);
  31. $this->success = false;
  32. return;
  33. }
  34. $this->loadConfig();
  35. // set capabilities based upon config strings set
  36. if(empty($this->conf['user']) ||
  37. empty($this->conf['password']) || empty($this->conf['database'])
  38. ) {
  39. $this->_debug("PgSQL err: insufficient configuration.", -1, __LINE__, __FILE__);
  40. $this->success = false;
  41. return;
  42. }
  43. $this->cando['addUser'] = $this->_chkcnf(
  44. array(
  45. 'getUserInfo',
  46. 'getGroups',
  47. 'addUser',
  48. 'getUserID',
  49. 'getGroupID',
  50. 'addGroup',
  51. 'addUserGroup'
  52. )
  53. );
  54. $this->cando['delUser'] = $this->_chkcnf(
  55. array(
  56. 'getUserID',
  57. 'delUser',
  58. 'delUserRefs'
  59. )
  60. );
  61. $this->cando['modLogin'] = $this->_chkcnf(
  62. array(
  63. 'getUserID',
  64. 'updateUser',
  65. 'UpdateTarget'
  66. )
  67. );
  68. $this->cando['modPass'] = $this->cando['modLogin'];
  69. $this->cando['modName'] = $this->cando['modLogin'];
  70. $this->cando['modMail'] = $this->cando['modLogin'];
  71. $this->cando['modGroups'] = $this->_chkcnf(
  72. array(
  73. 'getUserID',
  74. 'getGroups',
  75. 'getGroupID',
  76. 'addGroup',
  77. 'addUserGroup',
  78. 'delGroup',
  79. 'getGroupID',
  80. 'delUserGroup'
  81. )
  82. );
  83. /* getGroups is not yet supported
  84. $this->cando['getGroups'] = $this->_chkcnf(array('getGroups',
  85. 'getGroupID')); */
  86. $this->cando['getUsers'] = $this->_chkcnf(
  87. array(
  88. 'getUsers',
  89. 'getUserInfo',
  90. 'getGroups'
  91. )
  92. );
  93. $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'));
  94. }
  95. /**
  96. * Check if the given config strings are set
  97. *
  98. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  99. *
  100. * @param array $keys
  101. * @param bool $wop
  102. * @return bool
  103. */
  104. protected function _chkcnf($keys, $wop = false) {
  105. foreach($keys as $key) {
  106. if(empty($this->conf[$key])) return false;
  107. }
  108. return true;
  109. }
  110. /**
  111. * Counts users which meet certain $filter criteria.
  112. *
  113. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  114. *
  115. * @param array $filter filter criteria in item/pattern pairs
  116. * @return int count of found users.
  117. */
  118. public function getUserCount($filter = array()) {
  119. $rc = 0;
  120. if($this->_openDB()) {
  121. $sql = $this->_createSQLFilter($this->conf['getUsers'], $filter);
  122. // no equivalent of SQL_CALC_FOUND_ROWS in pgsql?
  123. if(($result = $this->_queryDB($sql))) {
  124. $rc = count($result);
  125. }
  126. $this->_closeDB();
  127. }
  128. return $rc;
  129. }
  130. /**
  131. * Bulk retrieval of user data
  132. *
  133. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  134. *
  135. * @param int $first index of first user to be returned
  136. * @param int $limit max number of users to be returned
  137. * @param array $filter array of field/pattern pairs
  138. * @return array userinfo (refer getUserData for internal userinfo details)
  139. */
  140. public function retrieveUsers($first = 0, $limit = 10, $filter = array()) {
  141. $out = array();
  142. if($this->_openDB()) {
  143. $this->_lockTables("READ");
  144. $sql = $this->_createSQLFilter($this->conf['getUsers'], $filter);
  145. $sql .= " ".$this->conf['SortOrder']." LIMIT $limit OFFSET $first";
  146. $result = $this->_queryDB($sql);
  147. foreach($result as $user)
  148. if(($info = $this->_getUserInfo($user['user'])))
  149. $out[$user['user']] = $info;
  150. $this->_unlockTables();
  151. $this->_closeDB();
  152. }
  153. return $out;
  154. }
  155. // @inherit function joinGroup($user, $group)
  156. // @inherit function leaveGroup($user, $group) {
  157. /**
  158. * Adds a user to a group.
  159. *
  160. * If $force is set to true non existing groups would be created.
  161. *
  162. * The database connection must already be established. Otherwise
  163. * this function does nothing and returns 'false'.
  164. *
  165. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  166. * @author Andreas Gohr <andi@splitbrain.org>
  167. *
  168. * @param string $user user to add to a group
  169. * @param string $group name of the group
  170. * @param bool $force create missing groups
  171. * @return bool true on success, false on error
  172. */
  173. protected function _addUserToGroup($user, $group, $force = false) {
  174. $newgroup = 0;
  175. if(($this->dbcon) && ($user)) {
  176. $gid = $this->_getGroupID($group);
  177. if(!$gid) {
  178. if($force) { // create missing groups
  179. $sql = str_replace('%{group}', addslashes($group), $this->conf['addGroup']);
  180. $this->_modifyDB($sql);
  181. //group should now exists try again to fetch it
  182. $gid = $this->_getGroupID($group);
  183. $newgroup = 1; // group newly created
  184. }
  185. }
  186. if(!$gid) return false; // group didn't exist and can't be created
  187. $sql = $this->conf['addUserGroup'];
  188. if(strpos($sql, '%{uid}') !== false) {
  189. $uid = $this->_getUserID($user);
  190. $sql = str_replace('%{uid}', addslashes($uid), $sql);
  191. }
  192. $sql = str_replace('%{user}', addslashes($user), $sql);
  193. $sql = str_replace('%{gid}', addslashes($gid), $sql);
  194. $sql = str_replace('%{group}', addslashes($group), $sql);
  195. if($this->_modifyDB($sql) !== false) return true;
  196. if($newgroup) { // remove previously created group on error
  197. $sql = str_replace('%{gid}', addslashes($gid), $this->conf['delGroup']);
  198. $sql = str_replace('%{group}', addslashes($group), $sql);
  199. $this->_modifyDB($sql);
  200. }
  201. }
  202. return false;
  203. }
  204. // @inherit function _delUserFromGroup($user $group)
  205. // @inherit function _getGroups($user)
  206. // @inherit function _getUserID($user)
  207. /**
  208. * Adds a new User to the database.
  209. *
  210. * The database connection must already be established
  211. * for this function to work. Otherwise it will return
  212. * 'false'.
  213. *
  214. * @param string $user login of the user
  215. * @param string $pwd encrypted password
  216. * @param string $name full name of the user
  217. * @param string $mail email address
  218. * @param array $grps array of groups the user should become member of
  219. * @return bool
  220. *
  221. * @author Andreas Gohr <andi@splitbrain.org>
  222. * @author Chris Smith <chris@jalakai.co.uk>
  223. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  224. */
  225. protected function _addUser($user, $pwd, $name, $mail, $grps) {
  226. if($this->dbcon && is_array($grps)) {
  227. $sql = str_replace('%{user}', addslashes($user), $this->conf['addUser']);
  228. $sql = str_replace('%{pass}', addslashes($pwd), $sql);
  229. $sql = str_replace('%{name}', addslashes($name), $sql);
  230. $sql = str_replace('%{email}', addslashes($mail), $sql);
  231. if($this->_modifyDB($sql)) {
  232. $uid = $this->_getUserID($user);
  233. } else {
  234. return false;
  235. }
  236. $group = '';
  237. $gid = false;
  238. if($uid) {
  239. foreach($grps as $group) {
  240. $gid = $this->_addUserToGroup($user, $group, 1);
  241. if($gid === false) break;
  242. }
  243. if($gid !== false){
  244. return true;
  245. } else {
  246. /* remove the new user and all group relations if a group can't
  247. * be assigned. Newly created groups will remain in the database
  248. * and won't be removed. This might create orphaned groups but
  249. * is not a big issue so we ignore this problem here.
  250. */
  251. $this->_delUser($user);
  252. $this->_debug("PgSQL err: Adding user '$user' to group '$group' failed.", -1, __LINE__, __FILE__);
  253. }
  254. }
  255. }
  256. return false;
  257. }
  258. /**
  259. * Opens a connection to a database and saves the handle for further
  260. * usage in the object. The successful call to this functions is
  261. * essential for most functions in this object.
  262. *
  263. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  264. *
  265. * @return bool
  266. */
  267. protected function _openDB() {
  268. if(!$this->dbcon) {
  269. $dsn = $this->conf['server'] ? 'host='.$this->conf['server'] : '';
  270. $dsn .= ' port='.$this->conf['port'];
  271. $dsn .= ' dbname='.$this->conf['database'];
  272. $dsn .= ' user='.$this->conf['user'];
  273. $dsn .= ' password='.$this->conf['password'];
  274. $con = @pg_connect($dsn);
  275. if($con) {
  276. $this->dbcon = $con;
  277. return true; // connection and database successfully opened
  278. } else {
  279. $this->_debug(
  280. "PgSQL err: Connection to {$this->conf['user']}@{$this->conf['server']} not possible.",
  281. -1, __LINE__, __FILE__
  282. );
  283. }
  284. return false; // connection failed
  285. }
  286. return true; // connection already open
  287. }
  288. /**
  289. * Closes a database connection.
  290. *
  291. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  292. */
  293. protected function _closeDB() {
  294. if($this->dbcon) {
  295. pg_close($this->dbcon);
  296. $this->dbcon = 0;
  297. }
  298. }
  299. /**
  300. * Sends a SQL query to the database and transforms the result into
  301. * an associative array.
  302. *
  303. * This function is only able to handle queries that returns a
  304. * table such as SELECT.
  305. *
  306. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  307. *
  308. * @param string $query SQL string that contains the query
  309. * @return array the result table
  310. */
  311. protected function _queryDB($query) {
  312. $resultarray = array();
  313. if($this->dbcon) {
  314. $result = @pg_query($this->dbcon, $query);
  315. if($result) {
  316. while(($t = pg_fetch_assoc($result)) !== false)
  317. $resultarray[] = $t;
  318. pg_free_result($result);
  319. return $resultarray;
  320. } else{
  321. $this->_debug('PgSQL err: '.pg_last_error($this->dbcon), -1, __LINE__, __FILE__);
  322. }
  323. }
  324. return false;
  325. }
  326. /**
  327. * Executes an update or insert query. This differs from the
  328. * MySQL one because it does NOT return the last insertID
  329. *
  330. * @author Andreas Gohr <andi@splitbrain.org>
  331. */
  332. protected function _modifyDB($query) {
  333. if($this->dbcon) {
  334. $result = @pg_query($this->dbcon, $query);
  335. if($result) {
  336. pg_free_result($result);
  337. return true;
  338. }
  339. $this->_debug('PgSQL err: '.pg_last_error($this->dbcon), -1, __LINE__, __FILE__);
  340. }
  341. return false;
  342. }
  343. /**
  344. * Start a transaction
  345. *
  346. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  347. *
  348. * @param string $mode could be 'READ' or 'WRITE'
  349. * @return bool
  350. */
  351. protected function _lockTables($mode) {
  352. if($this->dbcon) {
  353. $this->_modifyDB('BEGIN');
  354. return true;
  355. }
  356. return false;
  357. }
  358. /**
  359. * Commit a transaction
  360. *
  361. * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  362. */
  363. protected function _unlockTables() {
  364. if($this->dbcon) {
  365. $this->_modifyDB('COMMIT');
  366. return true;
  367. }
  368. return false;
  369. }
  370. /**
  371. * Escape a string for insertion into the database
  372. *
  373. * @author Andreas Gohr <andi@splitbrain.org>
  374. *
  375. * @param string $string The string to escape
  376. * @param bool $like Escape wildcard chars as well?
  377. * @return string
  378. */
  379. protected function _escape($string, $like = false) {
  380. $string = pg_escape_string($string);
  381. if($like) {
  382. $string = addcslashes($string, '%_');
  383. }
  384. return $string;
  385. }
  386. }