1<?php 2/** 3 * DokuWiki Plugin authwordpress (Auth Component) 4 * 5 * Provides authentication against a WordPress MySQL database backend 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; version 2 of the License 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * See the COPYING file in your DokuWiki folder for details 17 * 18 * @author Damien Regad <dregad@mantisbt.org> 19 * @copyright 2015 Damien Regad 20 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 21 * @version 1.1 22 * @link https://github.com/dregad/dokuwiki-authwordpress 23 */ 24 25 26// must be run within Dokuwiki 27if(!defined('DOKU_INC')) die(); 28 29/** 30 * WordPress password hashing framework 31 */ 32require_once('class-phpass.php'); 33 34/** 35 * Authentication class 36 */ 37class auth_plugin_authwordpress extends DokuWiki_Auth_Plugin { 38 39 /** 40 * SQL statement to retrieve User data from WordPress DB 41 * (including group memberships) 42 * '%prefix%' will be replaced by the actual prefix (from plugin config) 43 */ 44 protected $sql_wp_user_data = "SELECT 45 id, user_login, user_pass, user_email, display_name, 46 meta_value AS groups 47 FROM %prefix%users u 48 JOIN %prefix%usermeta m ON u.id = m.user_id AND meta_key = '%prefix%capabilities'"; 49 50 /** 51 * Wordpress database connection 52 */ 53 protected $db; 54 55 /** 56 * Users cache 57 */ 58 protected $users; 59 60 /** 61 * True if all users have been loaded in the cache 62 * @see $users 63 */ 64 protected $usersCached = false; 65 66 67 /** 68 * Constructor. 69 */ 70 public function __construct() { 71 parent::__construct(); 72 73 // Plugin capabilities 74 $this->cando['getUsers'] = true; 75 $this->cando['getUserCount'] = true; 76 77 // Try to establish a connection to the WordPress DB 78 // abort in case of failure 79 try { 80 $this->wp_connect(); 81 } 82 catch (Exception $e) { 83 msg(sprintf($this->getLang('error_connect_failed'), $e->getMessage())); 84 $this->success = false; 85 return; 86 } 87 88 // Initialize SQL query with configured prefix 89 $this->sql_wp_user_data = str_replace( 90 '%prefix%', 91 $this->getConf('prefix'), 92 $this->sql_wp_user_data 93 ); 94 95 $this->success = true; 96 } 97 98 99 /** 100 * Check user+password 101 * 102 * @param string $user the user name 103 * @param string $pass the clear text password 104 * @return bool 105 * 106 * @uses PasswordHash::CheckPassword WordPress password hasher 107 */ 108 public function checkPass($user, $pass) { 109 $data = $this->getUserData($user); 110 if ($data === false) { 111 return false; 112 } 113 114 $hasher = new PasswordHash(8, true); 115 $check = $hasher->CheckPassword($pass, $data['pass']); 116 dbglog("Password " . ($check ? 'OK' : 'Invalid')); 117 118 return $check; 119 } 120 121 /** 122 * Bulk retrieval of user data 123 * 124 * @param int $start index of first user to be returned 125 * @param int $limit max number of users to be returned 126 * @param array $filter array of field/pattern pairs 127 * @return array userinfo (refer getUserData for internal userinfo details) 128 */ 129 public function retrieveUsers($start = 0, $limit = 0, $filter = array()) { 130 msg($this->getLang('user_list_use_wordpress')); 131 if($filter) { 132 msg($this->getLang('error_filters_unsupported')); 133 } 134 135 $this->cacheAllUsers(); 136 return $this->users; 137 } 138 139 /** 140 * Return a count of the number of user which meet $filter criteria 141 * 142 * @param array $filter 143 * @return int 144 */ 145 public function getUserCount($filter = array()) { 146 $this->cacheAllUsers(); 147 return count($this->users); 148 } 149 150 151 /** 152 * Returns info about the given user 153 * 154 * @param string $user the user name 155 * @return array containing user data or false 156 */ 157 public function getUserData($user, $requireGroups=true) { 158 if(isset($this->users[$user])) { 159 return $this->users[$user]; 160 } 161 162 $sql = $this->sql_wp_user_data 163 . 'WHERE user_login = :user'; 164 165 $stmt = $this->db->prepare($sql); 166 $stmt->bindParam(':user', $user); 167 dbglog("Retrieving data for user '$user'\n$sql"); 168 169 if (!$stmt->execute()) { 170 // Query execution failed 171 $err = $stmt->errorInfo(); 172 dbglog("Error $err[1]: $err[2]"); 173 return false; 174 } 175 176 $user = $stmt->fetch(PDO::FETCH_ASSOC); 177 if ($user === false) { 178 // Unknown user 179 dbglog("Unknown user"); 180 return false; 181 } 182 183 return $this->cacheUser($user); 184 } 185 186 187 /** 188 * Connect to Wordpress database 189 * Initializes $db property as PDO object 190 */ 191 protected function wp_connect() { 192 if($this->db) { 193 // Already connected 194 return; 195 } 196 197 // Build connection string 198 $dsn = array( 199 'host=' . $this->getConf('hostname'), 200 'dbname=' . $this->getConf('database'), 201 ); 202 $port = $this->getConf('port'); 203 if ($port) { 204 $dsn[] = 'port=' . $port; 205 } 206 $dsn = 'mysql:' . implode(';', $dsn); 207 208 $this->db = new PDO($dsn, $this->getConf('username'), $this->getConf('password')); 209 } 210 211 /** 212 * Convert a Wordpress DB User row to DokuWiki user info array 213 * and stores it in the users cache 214 * 215 * @param array $user Raw Wordpress user table row 216 * @return array user data 217 */ 218 protected function cacheUser($row) { 219 global $conf; 220 221 $login = $row['user_login']; 222 223 // If the user is already cached, just return it 224 if(isset($this->users[$login])) { 225 return $this->users[$login]; 226 } 227 228 // Group membership - add DokuWiki's default group 229 $groups = array_keys(unserialize($row['groups'])); 230 if($this->getConf('usedefaultgroup')) { 231 $groups[] = $conf['defaultgroup']; 232 } 233 234 $info = array( 235 'user' => $login, 236 'name' => $row['display_name'], 237 'pass' => $row['user_pass'], 238 'mail' => $row['user_email'], 239 'grps' => $groups, 240 ); 241 242 $this->users[$login] = $info; 243 return $info; 244 } 245 246 /** 247 * Loads all Wordpress users into the cache 248 * 249 * @return void 250 */ 251 protected function cacheAllUsers() { 252 if($this->usersCached) { 253 return; 254 } 255 256 $stmt = $this->db->prepare($this->sql_wp_user_data); 257 $stmt->execute(); 258 259 foreach($stmt->fetchAll(PDO::FETCH_ASSOC) as $user) { 260 $this->cacheUser($user); 261 } 262 263 $this->usersCached = true; 264 } 265 266} 267 268// vim:ts=4:sw=4:noet: 269