aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--lib/plugins/authpdo/.travis.yml13
-rw-r--r--lib/plugins/authpdo/README27
-rw-r--r--lib/plugins/authpdo/_test/general.test.php33
-rw-r--r--lib/plugins/authpdo/_test/sqlite.test.php49
-rw-r--r--lib/plugins/authpdo/_test/test.sqlite3bin0 -> 14336 bytes
-rw-r--r--lib/plugins/authpdo/auth.php374
-rw-r--r--lib/plugins/authpdo/conf/default.php21
-rw-r--r--lib/plugins/authpdo/conf/metadata.php10
-rw-r--r--lib/plugins/authpdo/lang/en/lang.php16
-rw-r--r--lib/plugins/authpdo/lang/en/settings.php13
-rw-r--r--lib/plugins/authpdo/plugin.info.txt7
12 files changed, 564 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 7410ee1c3..14cc1f129 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@
!/lib/plugins/authldap
!/lib/plugins/authmysql
!/lib/plugins/authpgsql
+!/lib/plugins/authpdo
!/lib/plugins/authplain
!/lib/plugins/config
!/lib/plugins/extension
diff --git a/lib/plugins/authpdo/.travis.yml b/lib/plugins/authpdo/.travis.yml
new file mode 100644
index 000000000..80368cad3
--- /dev/null
+++ b/lib/plugins/authpdo/.travis.yml
@@ -0,0 +1,13 @@
+# Config file for travis-ci.org
+
+language: php
+php:
+ - "5.5"
+ - "5.4"
+ - "5.3"
+env:
+ - DOKUWIKI=master
+ - DOKUWIKI=stable
+before_install: wget https://raw.github.com/splitbrain/dokuwiki-travis/master/travis.sh
+install: sh travis.sh
+script: cd _test && phpunit --stderr --group plugin_authpdo \ No newline at end of file
diff --git a/lib/plugins/authpdo/README b/lib/plugins/authpdo/README
new file mode 100644
index 000000000..c99bfbf81
--- /dev/null
+++ b/lib/plugins/authpdo/README
@@ -0,0 +1,27 @@
+authpdo Plugin for DokuWiki
+
+Authenticate against a database via PDO
+
+All documentation for this plugin can be found at
+https://www.dokuwiki.org/plugin:authpdo
+
+If you install this plugin manually, make sure it is installed in
+lib/plugins/authpdo/ - if the folder is called different it
+will not work!
+
+Please refer to http://www.dokuwiki.org/plugins for additional info
+on how to install plugins in DokuWiki.
+
+----
+Copyright (C) Andreas Gohr <andi@splitbrain.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; version 2 of the License
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+See the COPYING file in your DokuWiki folder for details
diff --git a/lib/plugins/authpdo/_test/general.test.php b/lib/plugins/authpdo/_test/general.test.php
new file mode 100644
index 000000000..6c48b6957
--- /dev/null
+++ b/lib/plugins/authpdo/_test/general.test.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * General tests for the authpdo plugin
+ *
+ * @group plugin_authpdo
+ * @group plugins
+ */
+class general_plugin_authpdo_test extends DokuWikiTest {
+
+ /**
+ * Simple test to make sure the plugin.info.txt is in correct format
+ */
+ public function test_plugininfo() {
+ $file = __DIR__.'/../plugin.info.txt';
+ $this->assertFileExists($file);
+
+ $info = confToHash($file);
+
+ $this->assertArrayHasKey('base', $info);
+ $this->assertArrayHasKey('author', $info);
+ $this->assertArrayHasKey('email', $info);
+ $this->assertArrayHasKey('date', $info);
+ $this->assertArrayHasKey('name', $info);
+ $this->assertArrayHasKey('desc', $info);
+ $this->assertArrayHasKey('url', $info);
+
+ $this->assertEquals('authpdo', $info['base']);
+ $this->assertRegExp('/^https?:\/\//', $info['url']);
+ $this->assertTrue(mail_isvalid($info['email']));
+ $this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
+ $this->assertTrue(false !== strtotime($info['date']));
+ }
+}
diff --git a/lib/plugins/authpdo/_test/sqlite.test.php b/lib/plugins/authpdo/_test/sqlite.test.php
new file mode 100644
index 000000000..b60072d94
--- /dev/null
+++ b/lib/plugins/authpdo/_test/sqlite.test.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * General tests for the authpdo plugin
+ *
+ * @group plugin_authpdo
+ * @group plugins
+ */
+class sqlite_plugin_authpdo_test extends DokuWikiTest {
+
+ protected $dbfile;
+
+ public function setUp() {
+ parent::setUp();
+ $this->dbfile = tempnam('/tmp/', 'pluginpdo_test_');
+ copy(__DIR__ . '/test.sqlite3', $this->dbfile);
+
+ global $conf;
+
+ $conf['plugin']['authpdo']['debug'] = 1;
+ $conf['plugin']['authpdo']['dsn'] = 'sqlite:' . $this->dbfile;
+ $conf['plugin']['authpdo']['user'] = '';
+ $conf['plugin']['authpdo']['pass'] = '';
+
+
+ $conf['plugin']['authpdo']['select-user'] = 'SELECT id as uid, login as user, name, pass as clear, mail FROM user WHERE login = :user';
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ unlink($this->dbfile);
+ }
+
+ public function test_userinfo() {
+ global $conf;
+ $auth = new auth_plugin_authpdo();
+
+ // clear text pasword (with default config above
+ $this->assertFalse($auth->checkPass('nobody', 'nope'));
+ $this->assertFalse($auth->checkPass('admin', 'nope'));
+ $this->assertTrue($auth->checkPass('admin', 'password'));
+
+ // now with a hashed password
+ $conf['plugin']['authpdo']['select-user'] = 'SELECT id as uid, login as user, name, pass as hash, mail FROM user WHERE login = :user';
+ $this->assertFalse($auth->checkPass('admin', 'password'));
+ $this->assertFalse($auth->checkPass('user', md5('password')));
+
+ }
+}
diff --git a/lib/plugins/authpdo/_test/test.sqlite3 b/lib/plugins/authpdo/_test/test.sqlite3
new file mode 100644
index 000000000..403bf5f72
--- /dev/null
+++ b/lib/plugins/authpdo/_test/test.sqlite3
Binary files differ
diff --git a/lib/plugins/authpdo/auth.php b/lib/plugins/authpdo/auth.php
new file mode 100644
index 000000000..1325bdcff
--- /dev/null
+++ b/lib/plugins/authpdo/auth.php
@@ -0,0 +1,374 @@
+<?php
+/**
+ * DokuWiki Plugin authpdo (Auth Component)
+ *
+ * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+class auth_plugin_authpdo extends DokuWiki_Auth_Plugin {
+
+ /** @var PDO */
+ protected $pdo;
+
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ parent::__construct(); // for compatibility
+
+ if(!class_exists('PDO')) {
+ $this->_debug('PDO extension for PHP not found.', -1, __LINE__);
+ $this->success = false;
+ return;
+ }
+
+ if(!$this->getConf('dsn')) {
+ $this->_debug('No DSN specified', -1, __LINE__);
+ $this->success = false;
+ return;
+ }
+
+ try {
+ $this->pdo = new PDO(
+ $this->getConf('dsn'),
+ $this->getConf('user'),
+ $this->getConf('pass'),
+ array(
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
+ )
+ );
+ } catch(PDOException $e) {
+ $this->_debug($e);
+ $this->success = false;
+ return;
+ }
+
+ // FIXME set capabilities accordingly
+ //$this->cando['addUser'] = false; // can Users be created?
+ //$this->cando['delUser'] = false; // can Users be deleted?
+ //$this->cando['modLogin'] = false; // can login names be changed?
+ //$this->cando['modPass'] = false; // can passwords be changed?
+ //$this->cando['modName'] = false; // can real names be changed?
+ //$this->cando['modMail'] = false; // can emails be changed?
+ //$this->cando['modGroups'] = false; // can groups be changed?
+ //$this->cando['getUsers'] = false; // can a (filtered) list of users be retrieved?
+ //$this->cando['getUserCount']= false; // can the number of users be retrieved?
+ //$this->cando['getGroups'] = false; // can a list of available groups be retrieved?
+ //$this->cando['external'] = false; // does the module do external auth checking?
+ //$this->cando['logout'] = true; // can the user logout again? (eg. not possible with HTTP auth)
+
+ // FIXME intialize your auth system and set success to true, if successful
+ $this->success = true;
+ }
+
+ /**
+ * Check user+password
+ *
+ * May be ommited if trustExternal is used.
+ *
+ * @param string $user the user name
+ * @param string $pass the clear text password
+ * @return bool
+ */
+ public function checkPass($user, $pass) {
+
+ $data = $this->_selectUser($user);
+ if($data == false) return false;
+
+ if(isset($data['hash'])) {
+ // hashed password
+ $passhash = new PassHash();
+ return $passhash->verify_hash($pass, $data['hash']);
+ } else {
+ // clear text password in the database O_o
+ return ($pass == $data['clear']);
+ }
+ }
+
+ /**
+ * Return user info
+ *
+ * Returns info about the given user needs to contain
+ * at least these fields:
+ *
+ * name string full name of the user
+ * mail string email addres of the user
+ * grps array list of groups the user is in
+ *
+ * @param string $user the user name
+ * @param bool $requireGroups whether or not the returned data must include groups
+ * @return array containing user data or false
+ */
+ public function getUserData($user, $requireGroups = true) {
+ $data = $this->_selectUser($user);
+ if($data == false) return false;
+
+ if($requireGroups) {
+
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * Create a new User [implement only where required/possible]
+ *
+ * Returns false if the user already exists, null when an error
+ * occurred and true if everything went well.
+ *
+ * The new user HAS TO be added to the default group by this
+ * function!
+ *
+ * Set addUser capability when implemented
+ *
+ * @param string $user
+ * @param string $pass
+ * @param string $name
+ * @param string $mail
+ * @param null|array $grps
+ * @return bool|null
+ */
+ //public function createUser($user, $pass, $name, $mail, $grps = null) {
+ // FIXME implement
+ // return null;
+ //}
+
+ /**
+ * Modify user data [implement only where required/possible]
+ *
+ * Set the mod* capabilities according to the implemented features
+ *
+ * @param string $user nick of the user to be changed
+ * @param array $changes array of field/value pairs to be changed (password will be clear text)
+ * @return bool
+ */
+ //public function modifyUser($user, $changes) {
+ // FIXME implement
+ // return false;
+ //}
+
+ /**
+ * Delete one or more users [implement only where required/possible]
+ *
+ * Set delUser capability when implemented
+ *
+ * @param array $users
+ * @return int number of users deleted
+ */
+ //public function deleteUsers($users) {
+ // FIXME implement
+ // return false;
+ //}
+
+ /**
+ * Bulk retrieval of user data [implement only where required/possible]
+ *
+ * Set getUsers capability when implemented
+ *
+ * @param int $start index of first user to be returned
+ * @param int $limit max number of users to be returned
+ * @param array $filter array of field/pattern pairs, null for no filter
+ * @return array list of userinfo (refer getUserData for internal userinfo details)
+ */
+ //public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
+ // FIXME implement
+ // return array();
+ //}
+
+ /**
+ * Return a count of the number of user which meet $filter criteria
+ * [should be implemented whenever retrieveUsers is implemented]
+ *
+ * Set getUserCount capability when implemented
+ *
+ * @param array $filter array of field/pattern pairs, empty array for no filter
+ * @return int
+ */
+ //public function getUserCount($filter = array()) {
+ // FIXME implement
+ // return 0;
+ //}
+
+ /**
+ * Define a group [implement only where required/possible]
+ *
+ * Set addGroup capability when implemented
+ *
+ * @param string $group
+ * @return bool
+ */
+ //public function addGroup($group) {
+ // FIXME implement
+ // return false;
+ //}
+
+ /**
+ * Retrieve groups [implement only where required/possible]
+ *
+ * Set getGroups capability when implemented
+ *
+ * @param int $start
+ * @param int $limit
+ * @return array
+ */
+ //public function retrieveGroups($start = 0, $limit = 0) {
+ // FIXME implement
+ // return array();
+ //}
+
+ /**
+ * Return case sensitivity of the backend
+ *
+ * When your backend is caseinsensitive (eg. you can login with USER and
+ * user) then you need to overwrite this method and return false
+ *
+ * @return bool
+ */
+ public function isCaseSensitive() {
+ return true;
+ }
+
+ /**
+ * Sanitize a given username
+ *
+ * This function is applied to any user name that is given to
+ * the backend and should also be applied to any user name within
+ * the backend before returning it somewhere.
+ *
+ * This should be used to enforce username restrictions.
+ *
+ * @param string $user username
+ * @return string the cleaned username
+ */
+ public function cleanUser($user) {
+ return $user;
+ }
+
+ /**
+ * Sanitize a given groupname
+ *
+ * This function is applied to any groupname that is given to
+ * the backend and should also be applied to any groupname within
+ * the backend before returning it somewhere.
+ *
+ * This should be used to enforce groupname restrictions.
+ *
+ * Groupnames are to be passed without a leading '@' here.
+ *
+ * @param string $group groupname
+ * @return string the cleaned groupname
+ */
+ public function cleanGroup($group) {
+ return $group;
+ }
+
+ /**
+ * Check Session Cache validity [implement only where required/possible]
+ *
+ * DokuWiki caches user info in the user's session for the timespan defined
+ * in $conf['auth_security_timeout'].
+ *
+ * This makes sure slow authentication backends do not slow down DokuWiki.
+ * This also means that changes to the user database will not be reflected
+ * on currently logged in users.
+ *
+ * To accommodate for this, the user manager plugin will touch a reference
+ * file whenever a change is submitted. This function compares the filetime
+ * of this reference file with the time stored in the session.
+ *
+ * This reference file mechanism does not reflect changes done directly in
+ * the backend's database through other means than the user manager plugin.
+ *
+ * Fast backends might want to return always false, to force rechecks on
+ * each page load. Others might want to use their own checking here. If
+ * unsure, do not override.
+ *
+ * @param string $user - The username
+ * @return bool
+ */
+ //public function useSessionCache($user) {
+ // FIXME implement
+ //}
+
+ /**
+ * Select data of a specified user
+ *
+ * @param $user
+ * @return bool|array
+ */
+ protected function _selectUser($user) {
+ $sql = $this->getConf('select-user');
+
+ try {
+ $sth = $this->pdo->prepare($sql);
+ $sth->execute(array(':user' => $user));
+ $result = $sth->fetchAll();
+ $sth->closeCursor();
+ $sth = null;
+ } catch(PDOException $e) {
+ $this->_debug($e);
+ $result = array();
+ }
+ $found = count($result);
+ if($found == 0) return false;
+
+ if($found > 1) {
+ $this->_debug('Found more than one matching user', -1, __LINE__);
+ return false;
+ }
+
+ $data = array_shift($result);
+ $dataok = true;
+
+ if(!isset($data['user'])) {
+ $this->_debug("Statement did not return 'user' attribute", -1, __LINE__);
+ $dataok = false;
+ }
+ if(!isset($data['hash']) && !isset($data['clear'])) {
+ $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
+ $dataok = false;
+ }
+ if(!isset($data['name'])) {
+ $this->_debug("Statement did not return 'name' attribute", -1, __LINE__);
+ $dataok = false;
+ }
+ if(!isset($data['mail'])) {
+ $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__);
+ $dataok = false;
+ }
+
+ if(!$dataok) return false;
+ return $data;
+ }
+
+ /**
+ * Wrapper around msg() but outputs only when debug is enabled
+ *
+ * @param string|Exception $message
+ * @param int $err
+ * @param int $line
+ */
+ protected function _debug($message, $err = 0, $line = 0) {
+ if(!$this->getConf('debug')) return;
+ if(is_a($message, 'Exception')) {
+ $err = -1;
+ $line = $message->getLine();
+ $msg = $message->getMessage();
+ } else {
+ $msg = $message;
+ }
+
+ if(defined('DOKU_UNITTEST')) {
+ printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
+ } else {
+ msg('authpdo: ' . $msg, $err, $line, __FILE__);
+ }
+ }
+}
+
+// vim:ts=4:sw=4:et:
diff --git a/lib/plugins/authpdo/conf/default.php b/lib/plugins/authpdo/conf/default.php
new file mode 100644
index 000000000..22f8369d0
--- /dev/null
+++ b/lib/plugins/authpdo/conf/default.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Default settings for the authpdo plugin
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+//$conf['fixme'] = 'FIXME';
+
+$conf['debug'] = 0;
+$conf['dsn'] = '';
+$conf['user'] = '';
+$conf['pass'] = '';
+
+/**
+ * statement to select a single user identified by its login name given as :user
+ *
+ * return; user, name, mail, (clear|hash), [uid]
+ * other fields are returned but not used
+ */
+$conf['select-user'] = '';
diff --git a/lib/plugins/authpdo/conf/metadata.php b/lib/plugins/authpdo/conf/metadata.php
new file mode 100644
index 000000000..e020d5c45
--- /dev/null
+++ b/lib/plugins/authpdo/conf/metadata.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Options for the authpdo plugin
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+
+//$meta['fixme'] = array('string');
+
diff --git a/lib/plugins/authpdo/lang/en/lang.php b/lib/plugins/authpdo/lang/en/lang.php
new file mode 100644
index 000000000..de9f81252
--- /dev/null
+++ b/lib/plugins/authpdo/lang/en/lang.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * English language file for authpdo plugin
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+// menu entry for admin plugins
+// $lang['menu'] = 'Your menu entry';
+
+// custom language strings for the plugin
+// $lang['fixme'] = 'FIXME';
+
+
+
+//Setup VIM: ex: et ts=4 :
diff --git a/lib/plugins/authpdo/lang/en/settings.php b/lib/plugins/authpdo/lang/en/settings.php
new file mode 100644
index 000000000..503511ace
--- /dev/null
+++ b/lib/plugins/authpdo/lang/en/settings.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * english language file for authpdo plugin
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+// keys need to match the config setting name
+// $lang['fixme'] = 'FIXME';
+
+
+
+//Setup VIM: ex: et ts=4 :
diff --git a/lib/plugins/authpdo/plugin.info.txt b/lib/plugins/authpdo/plugin.info.txt
new file mode 100644
index 000000000..6784fd083
--- /dev/null
+++ b/lib/plugins/authpdo/plugin.info.txt
@@ -0,0 +1,7 @@
+base authpdo
+author Andreas Gohr
+email andi@splitbrain.org
+date 2016-01-29
+name authpdo plugin
+desc Authenticate against a database via PDO
+url https://www.dokuwiki.org/plugin:authpdo