xref: /plugin/authwordpress/auth.php (revision aa69c0cfeccdf94e7f4978370247034b547218a0)
124cd6f55SDamien Regad<?php
224cd6f55SDamien Regad/**
324cd6f55SDamien Regad * DokuWiki Plugin authwordpress (Auth Component)
424cd6f55SDamien Regad *
535dd80b8SDamien Regad * Provides authentication against a WordPress MySQL database backend
635dd80b8SDamien Regad *
735dd80b8SDamien Regad * This program is free software; you can redistribute it and/or modify
835dd80b8SDamien Regad * it under the terms of the GNU General Public License as published by
935dd80b8SDamien Regad * the Free Software Foundation; version 2 of the License
1035dd80b8SDamien Regad *
1135dd80b8SDamien Regad * This program is distributed in the hope that it will be useful,
1235dd80b8SDamien Regad * but WITHOUT ANY WARRANTY; without even the implied warranty of
1335dd80b8SDamien Regad * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1435dd80b8SDamien Regad * GNU General Public License for more details.
1535dd80b8SDamien Regad *
1635dd80b8SDamien Regad * See the COPYING file in your DokuWiki folder for details
1735dd80b8SDamien Regad *
1824cd6f55SDamien Regad * @author     Damien Regad <dregad@mantisbt.org>
1935dd80b8SDamien Regad * @copyright  2015 Damien Regad
2035dd80b8SDamien Regad * @license    GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
21b5b72c15SDamien Regad * @version    1.1
2235dd80b8SDamien Regad * @link       https://github.com/dregad/dokuwiki-authwordpress
2324cd6f55SDamien Regad */
2424cd6f55SDamien Regad
2535dd80b8SDamien Regad
2624cd6f55SDamien Regad// must be run within Dokuwiki
2724cd6f55SDamien Regadif(!defined('DOKU_INC')) die();
2824cd6f55SDamien Regad
2935dd80b8SDamien Regad/**
3035dd80b8SDamien Regad * WordPress password hashing framework
3135dd80b8SDamien Regad */
3235dd80b8SDamien Regadrequire_once('class-phpass.php');
3335dd80b8SDamien Regad
3435dd80b8SDamien Regad/**
3535dd80b8SDamien Regad * Authentication class
3635dd80b8SDamien Regad */
3724cd6f55SDamien Regadclass auth_plugin_authwordpress extends DokuWiki_Auth_Plugin {
3824cd6f55SDamien Regad
3935dd80b8SDamien Regad	/**
4035dd80b8SDamien Regad	 * SQL statement to retrieve User data from WordPress DB
4135dd80b8SDamien Regad	 * (including group memberships)
42b5b72c15SDamien Regad	 * '%prefix%' will be replaced by the actual prefix (from plugin config)
4335dd80b8SDamien Regad	 */
440341717fSDamien Regad	protected $sql_wp_user_data = "SELECT
4535dd80b8SDamien Regad			id, user_login, user_pass, user_email, display_name,
4635dd80b8SDamien Regad			meta_value AS groups
47b5b72c15SDamien Regad		FROM %prefix%users u
482ef3e6a1SDamien Regad		JOIN %prefix%usermeta m ON u.id = m.user_id AND meta_key = '%prefix%capabilities'";
4924cd6f55SDamien Regad
5024cd6f55SDamien Regad	/**
51015f33b2SDamien Regad	 * Wordpress database connection
52015f33b2SDamien Regad	 */
530341717fSDamien Regad	protected $db;
54015f33b2SDamien Regad
55d35aa3ecSDamien Regad	/**
56d35aa3ecSDamien Regad	 * Users cache
57d35aa3ecSDamien Regad	 */
58d35aa3ecSDamien Regad	protected $users;
59d35aa3ecSDamien Regad
60d35aa3ecSDamien Regad	/**
61d35aa3ecSDamien Regad	 * True if all users have been loaded in the cache
62d35aa3ecSDamien Regad	 * @see $users
63d35aa3ecSDamien Regad	 */
64d35aa3ecSDamien Regad	protected $usersCached = false;
65d35aa3ecSDamien Regad
66015f33b2SDamien Regad
67015f33b2SDamien Regad	/**
6824cd6f55SDamien Regad	 * Constructor.
6924cd6f55SDamien Regad	 */
7024cd6f55SDamien Regad	public function __construct() {
7135dd80b8SDamien Regad		parent::__construct();
7224cd6f55SDamien Regad
73*aa69c0cfSDamien Regad		// Plugin capabilities
7416ceb666SDamien Regad		$this->cando['getUsers'] = true;
75*aa69c0cfSDamien Regad		$this->cando['getUserCount'] = true;
7616ceb666SDamien Regad
7735dd80b8SDamien Regad		// Try to establish a connection to the WordPress DB
7835dd80b8SDamien Regad		// abort in case of failure
7935dd80b8SDamien Regad		try {
805a360df9SDamien Regad			$this->wp_connect();
8135dd80b8SDamien Regad		}
8235dd80b8SDamien Regad		catch (Exception $e) {
8335dd80b8SDamien Regad			msg(sprintf($this->getLang('error_connect_failed'), $e->getMessage()));
8435dd80b8SDamien Regad			$this->success = false;
8535dd80b8SDamien Regad			return;
8635dd80b8SDamien Regad		}
8724cd6f55SDamien Regad
88b5b72c15SDamien Regad		// Initialize SQL query with configured prefix
89b5b72c15SDamien Regad		$this->sql_wp_user_data = str_replace(
90b5b72c15SDamien Regad			'%prefix%',
91b5b72c15SDamien Regad			$this->getConf('prefix'),
92b5b72c15SDamien Regad			$this->sql_wp_user_data
93b5b72c15SDamien Regad		);
94b5b72c15SDamien Regad
9524cd6f55SDamien Regad		$this->success = true;
9624cd6f55SDamien Regad	}
9724cd6f55SDamien Regad
9824cd6f55SDamien Regad
9924cd6f55SDamien Regad	/**
10024cd6f55SDamien Regad	 * Check user+password
10124cd6f55SDamien Regad	 *
10224cd6f55SDamien Regad	 * @param   string $user the user name
10324cd6f55SDamien Regad	 * @param   string $pass the clear text password
10424cd6f55SDamien Regad	 * @return  bool
10535dd80b8SDamien Regad	 *
10635dd80b8SDamien Regad	 * @uses PasswordHash::CheckPassword WordPress password hasher
10724cd6f55SDamien Regad	 */
10824cd6f55SDamien Regad	public function checkPass($user, $pass) {
10935dd80b8SDamien Regad		$data = $this->getUserData($user);
11035dd80b8SDamien Regad		if ($data === false) {
11135dd80b8SDamien Regad			return false;
11224cd6f55SDamien Regad		}
11324cd6f55SDamien Regad
11435dd80b8SDamien Regad		$hasher = new PasswordHash(8, true);
115eed09871SDamien Regad		$check = $hasher->CheckPassword($pass, $data['pass']);
116eed09871SDamien Regad		dbglog("Password " . ($check ? 'OK' : 'Invalid'));
117eed09871SDamien Regad
118eed09871SDamien Regad		return $check;
11935dd80b8SDamien Regad	}
12035dd80b8SDamien Regad
12116ceb666SDamien Regad	/**
12216ceb666SDamien Regad	 * Bulk retrieval of user data
12316ceb666SDamien Regad	 *
12416ceb666SDamien Regad	 * @param   int   $start index of first user to be returned
12516ceb666SDamien Regad	 * @param   int   $limit max number of users to be returned
12616ceb666SDamien Regad	 * @param   array $filter array of field/pattern pairs
12716ceb666SDamien Regad	 * @return  array userinfo (refer getUserData for internal userinfo details)
12816ceb666SDamien Regad	 */
12916ceb666SDamien Regad	public function retrieveUsers($start = 0, $limit = 0, $filter = array()) {
13016ceb666SDamien Regad		msg($this->getLang('user_list_use_wordpress'));
1312ef3e6a1SDamien Regad		if($filter) {
1322ef3e6a1SDamien Regad			msg($this->getLang('error_filters_unsupported'));
1332ef3e6a1SDamien Regad		}
1342ef3e6a1SDamien Regad
135d35aa3ecSDamien Regad		$this->cacheAllUsers();
136d35aa3ecSDamien Regad		return $this->users;
13716ceb666SDamien Regad	}
13816ceb666SDamien Regad
139*aa69c0cfSDamien Regad	/**
140*aa69c0cfSDamien Regad	 * Return a count of the number of user which meet $filter criteria
141*aa69c0cfSDamien Regad	 *
142*aa69c0cfSDamien Regad	 * @param array $filter
143*aa69c0cfSDamien Regad	 * @return int
144*aa69c0cfSDamien Regad	 */
145*aa69c0cfSDamien Regad	public function getUserCount($filter = array()) {
146*aa69c0cfSDamien Regad		$this->cacheAllUsers();
147*aa69c0cfSDamien Regad		return count($this->users);
148*aa69c0cfSDamien Regad	}
149*aa69c0cfSDamien Regad
15035dd80b8SDamien Regad
15124cd6f55SDamien Regad	/**
15235dd80b8SDamien Regad	 * Returns info about the given user
15324cd6f55SDamien Regad	 *
15424cd6f55SDamien Regad	 * @param   string $user the user name
15524cd6f55SDamien Regad	 * @return  array containing user data or false
15624cd6f55SDamien Regad	 */
1570341717fSDamien Regad	public function getUserData($user, $requireGroups=true) {
158d35aa3ecSDamien Regad		if(isset($this->users[$user])) {
159d35aa3ecSDamien Regad			return $this->users[$user];
160d35aa3ecSDamien Regad		}
161d35aa3ecSDamien Regad
1622ef3e6a1SDamien Regad		$sql = $this->sql_wp_user_data
1632ef3e6a1SDamien Regad			. 'WHERE user_login = :user';
16435dd80b8SDamien Regad
1652ef3e6a1SDamien Regad		$stmt = $this->db->prepare($sql);
16635dd80b8SDamien Regad		$stmt->bindParam(':user', $user);
1672ef3e6a1SDamien Regad		dbglog("Retrieving data for user '$user'\n$sql");
16835dd80b8SDamien Regad
16935dd80b8SDamien Regad		if (!$stmt->execute()) {
1709520968dSDamien Regad			// Query execution failed
171eed09871SDamien Regad			$err = $stmt->errorInfo();
172eed09871SDamien Regad			dbglog("Error $err[1]: $err[2]");
17324cd6f55SDamien Regad			return false;
17424cd6f55SDamien Regad		}
1759520968dSDamien Regad
1769520968dSDamien Regad		$user = $stmt->fetch(PDO::FETCH_ASSOC);
1779520968dSDamien Regad		if ($user === false) {
1789520968dSDamien Regad			// Unknown user
179eed09871SDamien Regad			dbglog("Unknown user");
1809520968dSDamien Regad			return false;
1819520968dSDamien Regad		}
18224cd6f55SDamien Regad
183d35aa3ecSDamien Regad		return $this->cacheUser($user);
18424cd6f55SDamien Regad	}
18524cd6f55SDamien Regad
18624cd6f55SDamien Regad
18724cd6f55SDamien Regad	/**
18835dd80b8SDamien Regad	 * Connect to Wordpress database
189015f33b2SDamien Regad	 * Initializes $db property as PDO object
19024cd6f55SDamien Regad	 */
1910341717fSDamien Regad	protected function wp_connect() {
192cb81639bSDamien Regad		if($this->db) {
193cb81639bSDamien Regad			// Already connected
194cb81639bSDamien Regad			return;
195cb81639bSDamien Regad		}
196cb81639bSDamien Regad
197cb81639bSDamien Regad		// Build connection string
19835dd80b8SDamien Regad		$dsn = array(
19935dd80b8SDamien Regad			'host=' . $this->getConf('hostname'),
20035dd80b8SDamien Regad			'dbname=' . $this->getConf('database'),
20135dd80b8SDamien Regad		);
20235dd80b8SDamien Regad		$port = $this->getConf('port');
20335dd80b8SDamien Regad		if ($port) {
20435dd80b8SDamien Regad			$dsn[] = 'port=' . $port;
20535dd80b8SDamien Regad		}
20635dd80b8SDamien Regad		$dsn = 'mysql:' . implode(';', $dsn);
20735dd80b8SDamien Regad
208015f33b2SDamien Regad		$this->db = new PDO($dsn, $this->getConf('username'), $this->getConf('password'));
20924cd6f55SDamien Regad	}
21024cd6f55SDamien Regad
211d1f83a80SDamien Regad	/**
212d1f83a80SDamien Regad	 * Convert a Wordpress DB User row to DokuWiki user info array
213d35aa3ecSDamien Regad	 * and stores it in the users cache
214d1f83a80SDamien Regad	 *
215d1f83a80SDamien Regad	 * @param  array $user Raw Wordpress user table row
216d1f83a80SDamien Regad	 * @return array user data
217d1f83a80SDamien Regad	 */
218d35aa3ecSDamien Regad	protected function cacheUser($row) {
219d1f83a80SDamien Regad		global $conf;
220d1f83a80SDamien Regad
221d35aa3ecSDamien Regad		$login = $row['user_login'];
222d35aa3ecSDamien Regad
223d35aa3ecSDamien Regad		// If the user is already cached, just return it
224d35aa3ecSDamien Regad		if(isset($this->users[$login])) {
225d35aa3ecSDamien Regad			return $this->users[$login];
226d35aa3ecSDamien Regad		}
227d35aa3ecSDamien Regad
228d1f83a80SDamien Regad		// Group membership - add DokuWiki's default group
229d35aa3ecSDamien Regad		$groups = array_keys(unserialize($row['groups']));
230d1f83a80SDamien Regad		if($this->getConf('usedefaultgroup')) {
231d1f83a80SDamien Regad			$groups[] = $conf['defaultgroup'];
232d1f83a80SDamien Regad		}
233d1f83a80SDamien Regad
234d1f83a80SDamien Regad		$info = array(
235d35aa3ecSDamien Regad			'user' => $login,
236d35aa3ecSDamien Regad			'name' => $row['display_name'],
237d35aa3ecSDamien Regad			'pass' => $row['user_pass'],
238d35aa3ecSDamien Regad			'mail' => $row['user_email'],
239d1f83a80SDamien Regad			'grps' => $groups,
240d1f83a80SDamien Regad		);
241d35aa3ecSDamien Regad
242d35aa3ecSDamien Regad		$this->users[$login] = $info;
243d1f83a80SDamien Regad		return $info;
244d1f83a80SDamien Regad	}
245d1f83a80SDamien Regad
246d35aa3ecSDamien Regad	/**
247d35aa3ecSDamien Regad	 * Loads all Wordpress users into the cache
248d35aa3ecSDamien Regad	 *
249d35aa3ecSDamien Regad	 * @return void
250d35aa3ecSDamien Regad	 */
251d35aa3ecSDamien Regad	protected function cacheAllUsers() {
252d35aa3ecSDamien Regad		if($this->usersCached) {
253d35aa3ecSDamien Regad			return;
254d35aa3ecSDamien Regad		}
255d35aa3ecSDamien Regad
256d35aa3ecSDamien Regad		$stmt = $this->db->prepare($this->sql_wp_user_data);
257d35aa3ecSDamien Regad		$stmt->execute();
258d35aa3ecSDamien Regad
259d35aa3ecSDamien Regad		foreach($stmt->fetchAll(PDO::FETCH_ASSOC) as $user) {
260d35aa3ecSDamien Regad			$this->cacheUser($user);
261d35aa3ecSDamien Regad		}
262d35aa3ecSDamien Regad
263d35aa3ecSDamien Regad		$this->usersCached = true;
264d35aa3ecSDamien Regad	}
265d35aa3ecSDamien Regad
26624cd6f55SDamien Regad}
26724cd6f55SDamien Regad
2680e6cb03cSDamien Regad// vim:ts=4:sw=4:noet:
269