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 $this->cando['getUsers'] = true; 74 75 // Try to establish a connection to the WordPress DB 76 // abort in case of failure 77 try { 78 $this->wp_connect(); 79 } 80 catch (Exception $e) { 81 msg(sprintf($this->getLang('error_connect_failed'), $e->getMessage())); 82 $this->success = false; 83 return; 84 } 85 86 // Initialize SQL query with configured prefix 87 $this->sql_wp_user_data = str_replace( 88 '%prefix%', 89 $this->getConf('prefix'), 90 $this->sql_wp_user_data 91 ); 92 93 $this->success = true; 94 } 95 96 97 /** 98 * Check user+password 99 * 100 * @param string $user the user name 101 * @param string $pass the clear text password 102 * @return bool 103 * 104 * @uses PasswordHash::CheckPassword WordPress password hasher 105 */ 106 public function checkPass($user, $pass) { 107 $data = $this->getUserData($user); 108 if ($data === false) { 109 return false; 110 } 111 112 $hasher = new PasswordHash(8, true); 113 $check = $hasher->CheckPassword($pass, $data['pass']); 114 dbglog("Password " . ($check ? 'OK' : 'Invalid')); 115 116 return $check; 117 } 118 119 /** 120 * Bulk retrieval of user data 121 * 122 * @param int $start index of first user to be returned 123 * @param int $limit max number of users to be returned 124 * @param array $filter array of field/pattern pairs 125 * @return array userinfo (refer getUserData for internal userinfo details) 126 */ 127 public function retrieveUsers($start = 0, $limit = 0, $filter = array()) { 128 msg($this->getLang('user_list_use_wordpress')); 129 if($filter) { 130 msg($this->getLang('error_filters_unsupported')); 131 } 132 133 $this->cacheAllUsers(); 134 return $this->users; 135 } 136 137 138 /** 139 * Returns info about the given user 140 * 141 * @param string $user the user name 142 * @return array containing user data or false 143 */ 144 public function getUserData($user, $requireGroups=true) { 145 if(isset($this->users[$user])) { 146 return $this->users[$user]; 147 } 148 149 $sql = $this->sql_wp_user_data 150 . 'WHERE user_login = :user'; 151 152 $stmt = $this->db->prepare($sql); 153 $stmt->bindParam(':user', $user); 154 dbglog("Retrieving data for user '$user'\n$sql"); 155 156 if (!$stmt->execute()) { 157 // Query execution failed 158 $err = $stmt->errorInfo(); 159 dbglog("Error $err[1]: $err[2]"); 160 return false; 161 } 162 163 $user = $stmt->fetch(PDO::FETCH_ASSOC); 164 if ($user === false) { 165 // Unknown user 166 dbglog("Unknown user"); 167 return false; 168 } 169 170 return $this->cacheUser($user); 171 } 172 173 174 /** 175 * Connect to Wordpress database 176 * Initializes $db property as PDO object 177 */ 178 protected function wp_connect() { 179 if($this->db) { 180 // Already connected 181 return; 182 } 183 184 // Build connection string 185 $dsn = array( 186 'host=' . $this->getConf('hostname'), 187 'dbname=' . $this->getConf('database'), 188 ); 189 $port = $this->getConf('port'); 190 if ($port) { 191 $dsn[] = 'port=' . $port; 192 } 193 $dsn = 'mysql:' . implode(';', $dsn); 194 195 $this->db = new PDO($dsn, $this->getConf('username'), $this->getConf('password')); 196 } 197 198 /** 199 * Convert a Wordpress DB User row to DokuWiki user info array 200 * and stores it in the users cache 201 * 202 * @param array $user Raw Wordpress user table row 203 * @return array user data 204 */ 205 protected function cacheUser($row) { 206 global $conf; 207 208 $login = $row['user_login']; 209 210 // If the user is already cached, just return it 211 if(isset($this->users[$login])) { 212 return $this->users[$login]; 213 } 214 215 // Group membership - add DokuWiki's default group 216 $groups = array_keys(unserialize($row['groups'])); 217 if($this->getConf('usedefaultgroup')) { 218 $groups[] = $conf['defaultgroup']; 219 } 220 221 $info = array( 222 'user' => $login, 223 'name' => $row['display_name'], 224 'pass' => $row['user_pass'], 225 'mail' => $row['user_email'], 226 'grps' => $groups, 227 ); 228 229 $this->users[$login] = $info; 230 return $info; 231 } 232 233 /** 234 * Loads all Wordpress users into the cache 235 * 236 * @return void 237 */ 238 protected function cacheAllUsers() { 239 if($this->usersCached) { 240 return; 241 } 242 243 $stmt = $this->db->prepare($this->sql_wp_user_data); 244 $stmt->execute(); 245 246 foreach($stmt->fetchAll(PDO::FETCH_ASSOC) as $user) { 247 $this->cacheUser($user); 248 } 249 250 $this->usersCached = true; 251 } 252 253} 254 255// vim:ts=4:sw=4:noet: 256