xref: /plugin/authwordpress/auth.php (revision aa69c0cfeccdf94e7f4978370247034b547218a0)
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