AuthPlugin.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <?php
  2. namespace dokuwiki\Extension;
  3. /**
  4. * Auth Plugin Prototype
  5. *
  6. * allows to authenticate users in a plugin
  7. *
  8. * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
  9. * @author Chris Smith <chris@jalakai.co.uk>
  10. * @author Jan Schumann <js@jschumann-it.com>
  11. */
  12. abstract class AuthPlugin extends Plugin
  13. {
  14. public $success = true;
  15. /**
  16. * Possible things an auth backend module may be able to
  17. * do. The things a backend can do need to be set to true
  18. * in the constructor.
  19. */
  20. protected $cando = array(
  21. 'addUser' => false, // can Users be created?
  22. 'delUser' => false, // can Users be deleted?
  23. 'modLogin' => false, // can login names be changed?
  24. 'modPass' => false, // can passwords be changed?
  25. 'modName' => false, // can real names be changed?
  26. 'modMail' => false, // can emails be changed?
  27. 'modGroups' => false, // can groups be changed?
  28. 'getUsers' => false, // can a (filtered) list of users be retrieved?
  29. 'getUserCount' => false, // can the number of users be retrieved?
  30. 'getGroups' => false, // can a list of available groups be retrieved?
  31. 'external' => false, // does the module do external auth checking?
  32. 'logout' => true, // can the user logout again? (eg. not possible with HTTP auth)
  33. );
  34. /**
  35. * Constructor.
  36. *
  37. * Carry out sanity checks to ensure the object is
  38. * able to operate. Set capabilities in $this->cando
  39. * array here
  40. *
  41. * For future compatibility, sub classes should always include a call
  42. * to parent::__constructor() in their constructors!
  43. *
  44. * Set $this->success to false if checks fail
  45. *
  46. * @author Christopher Smith <chris@jalakai.co.uk>
  47. */
  48. public function __construct()
  49. {
  50. // the base class constructor does nothing, derived class
  51. // constructors do the real work
  52. }
  53. /**
  54. * Available Capabilities. [ DO NOT OVERRIDE ]
  55. *
  56. * For introspection/debugging
  57. *
  58. * @author Christopher Smith <chris@jalakai.co.uk>
  59. * @return array
  60. */
  61. public function getCapabilities()
  62. {
  63. return array_keys($this->cando);
  64. }
  65. /**
  66. * Capability check. [ DO NOT OVERRIDE ]
  67. *
  68. * Checks the capabilities set in the $this->cando array and
  69. * some pseudo capabilities (shortcutting access to multiple
  70. * ones)
  71. *
  72. * ususal capabilities start with lowercase letter
  73. * shortcut capabilities start with uppercase letter
  74. *
  75. * @author Andreas Gohr <andi@splitbrain.org>
  76. * @param string $cap the capability to check
  77. * @return bool
  78. */
  79. public function canDo($cap)
  80. {
  81. switch ($cap) {
  82. case 'Profile':
  83. // can at least one of the user's properties be changed?
  84. return ($this->cando['modPass'] ||
  85. $this->cando['modName'] ||
  86. $this->cando['modMail']);
  87. break;
  88. case 'UserMod':
  89. // can at least anything be changed?
  90. return ($this->cando['modPass'] ||
  91. $this->cando['modName'] ||
  92. $this->cando['modMail'] ||
  93. $this->cando['modLogin'] ||
  94. $this->cando['modGroups'] ||
  95. $this->cando['modMail']);
  96. break;
  97. default:
  98. // print a helping message for developers
  99. if (!isset($this->cando[$cap])) {
  100. msg("Check for unknown capability '$cap' - Do you use an outdated Plugin?", -1);
  101. }
  102. return $this->cando[$cap];
  103. }
  104. }
  105. /**
  106. * Trigger the AUTH_USERDATA_CHANGE event and call the modification function. [ DO NOT OVERRIDE ]
  107. *
  108. * You should use this function instead of calling createUser, modifyUser or
  109. * deleteUsers directly. The event handlers can prevent the modification, for
  110. * example for enforcing a user name schema.
  111. *
  112. * @author Gabriel Birke <birke@d-scribe.de>
  113. * @param string $type Modification type ('create', 'modify', 'delete')
  114. * @param array $params Parameters for the createUser, modifyUser or deleteUsers method.
  115. * The content of this array depends on the modification type
  116. * @return bool|null|int Result from the modification function or false if an event handler has canceled the action
  117. */
  118. public function triggerUserMod($type, $params)
  119. {
  120. $validTypes = array(
  121. 'create' => 'createUser',
  122. 'modify' => 'modifyUser',
  123. 'delete' => 'deleteUsers',
  124. );
  125. if (empty($validTypes[$type])) {
  126. return false;
  127. }
  128. $result = false;
  129. $eventdata = array('type' => $type, 'params' => $params, 'modification_result' => null);
  130. $evt = new Event('AUTH_USER_CHANGE', $eventdata);
  131. if ($evt->advise_before(true)) {
  132. $result = call_user_func_array(array($this, $validTypes[$type]), $evt->data['params']);
  133. $evt->data['modification_result'] = $result;
  134. }
  135. $evt->advise_after();
  136. unset($evt);
  137. return $result;
  138. }
  139. /**
  140. * Log off the current user [ OPTIONAL ]
  141. *
  142. * Is run in addition to the ususal logoff method. Should
  143. * only be needed when trustExternal is implemented.
  144. *
  145. * @see auth_logoff()
  146. * @author Andreas Gohr <andi@splitbrain.org>
  147. */
  148. public function logOff()
  149. {
  150. }
  151. /**
  152. * Do all authentication [ OPTIONAL ]
  153. *
  154. * Set $this->cando['external'] = true when implemented
  155. *
  156. * If this function is implemented it will be used to
  157. * authenticate a user - all other DokuWiki internals
  158. * will not be used for authenticating (except this
  159. * function returns null, in which case, DokuWiki will
  160. * still run auth_login as a fallback, which may call
  161. * checkPass()). If this function is not returning null,
  162. * implementing checkPass() is not needed here anymore.
  163. *
  164. * The function can be used to authenticate against third
  165. * party cookies or Apache auth mechanisms and replaces
  166. * the auth_login() function
  167. *
  168. * The function will be called with or without a set
  169. * username. If the Username is given it was called
  170. * from the login form and the given credentials might
  171. * need to be checked. If no username was given it
  172. * the function needs to check if the user is logged in
  173. * by other means (cookie, environment).
  174. *
  175. * The function needs to set some globals needed by
  176. * DokuWiki like auth_login() does.
  177. *
  178. * @see auth_login()
  179. * @author Andreas Gohr <andi@splitbrain.org>
  180. *
  181. * @param string $user Username
  182. * @param string $pass Cleartext Password
  183. * @param bool $sticky Cookie should not expire
  184. * @return bool true on successful auth,
  185. * null on unknown result (fallback to checkPass)
  186. */
  187. public function trustExternal($user, $pass, $sticky = false)
  188. {
  189. /* some example:
  190. global $USERINFO;
  191. global $conf;
  192. $sticky ? $sticky = true : $sticky = false; //sanity check
  193. // do the checking here
  194. // set the globals if authed
  195. $USERINFO['name'] = 'FIXME';
  196. $USERINFO['mail'] = 'FIXME';
  197. $USERINFO['grps'] = array('FIXME');
  198. $_SERVER['REMOTE_USER'] = $user;
  199. $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
  200. $_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass;
  201. $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
  202. return true;
  203. */
  204. }
  205. /**
  206. * Check user+password [ MUST BE OVERRIDDEN ]
  207. *
  208. * Checks if the given user exists and the given
  209. * plaintext password is correct
  210. *
  211. * May be ommited if trustExternal is used.
  212. *
  213. * @author Andreas Gohr <andi@splitbrain.org>
  214. * @param string $user the user name
  215. * @param string $pass the clear text password
  216. * @return bool
  217. */
  218. public function checkPass($user, $pass)
  219. {
  220. msg("no valid authorisation system in use", -1);
  221. return false;
  222. }
  223. /**
  224. * Return user info [ MUST BE OVERRIDDEN ]
  225. *
  226. * Returns info about the given user needs to contain
  227. * at least these fields:
  228. *
  229. * name string full name of the user
  230. * mail string email address of the user
  231. * grps array list of groups the user is in
  232. *
  233. * @author Andreas Gohr <andi@splitbrain.org>
  234. * @param string $user the user name
  235. * @param bool $requireGroups whether or not the returned data must include groups
  236. * @return false|array containing user data or false
  237. */
  238. public function getUserData($user, $requireGroups = true)
  239. {
  240. if (!$this->cando['external']) msg("no valid authorisation system in use", -1);
  241. return false;
  242. }
  243. /**
  244. * Create a new User [implement only where required/possible]
  245. *
  246. * Returns false if the user already exists, null when an error
  247. * occurred and true if everything went well.
  248. *
  249. * The new user HAS TO be added to the default group by this
  250. * function!
  251. *
  252. * Set addUser capability when implemented
  253. *
  254. * @author Andreas Gohr <andi@splitbrain.org>
  255. * @param string $user
  256. * @param string $pass
  257. * @param string $name
  258. * @param string $mail
  259. * @param null|array $grps
  260. * @return bool|null
  261. */
  262. public function createUser($user, $pass, $name, $mail, $grps = null)
  263. {
  264. msg("authorisation method does not allow creation of new users", -1);
  265. return null;
  266. }
  267. /**
  268. * Modify user data [implement only where required/possible]
  269. *
  270. * Set the mod* capabilities according to the implemented features
  271. *
  272. * @author Chris Smith <chris@jalakai.co.uk>
  273. * @param string $user nick of the user to be changed
  274. * @param array $changes array of field/value pairs to be changed (password will be clear text)
  275. * @return bool
  276. */
  277. public function modifyUser($user, $changes)
  278. {
  279. msg("authorisation method does not allow modifying of user data", -1);
  280. return false;
  281. }
  282. /**
  283. * Delete one or more users [implement only where required/possible]
  284. *
  285. * Set delUser capability when implemented
  286. *
  287. * @author Chris Smith <chris@jalakai.co.uk>
  288. * @param array $users
  289. * @return int number of users deleted
  290. */
  291. public function deleteUsers($users)
  292. {
  293. msg("authorisation method does not allow deleting of users", -1);
  294. return 0;
  295. }
  296. /**
  297. * Return a count of the number of user which meet $filter criteria
  298. * [should be implemented whenever retrieveUsers is implemented]
  299. *
  300. * Set getUserCount capability when implemented
  301. *
  302. * @author Chris Smith <chris@jalakai.co.uk>
  303. * @param array $filter array of field/pattern pairs, empty array for no filter
  304. * @return int
  305. */
  306. public function getUserCount($filter = array())
  307. {
  308. msg("authorisation method does not provide user counts", -1);
  309. return 0;
  310. }
  311. /**
  312. * Bulk retrieval of user data [implement only where required/possible]
  313. *
  314. * Set getUsers capability when implemented
  315. *
  316. * @author Chris Smith <chris@jalakai.co.uk>
  317. * @param int $start index of first user to be returned
  318. * @param int $limit max number of users to be returned, 0 for unlimited
  319. * @param array $filter array of field/pattern pairs, null for no filter
  320. * @return array list of userinfo (refer getUserData for internal userinfo details)
  321. */
  322. public function retrieveUsers($start = 0, $limit = 0, $filter = null)
  323. {
  324. msg("authorisation method does not support mass retrieval of user data", -1);
  325. return array();
  326. }
  327. /**
  328. * Define a group [implement only where required/possible]
  329. *
  330. * Set addGroup capability when implemented
  331. *
  332. * @author Chris Smith <chris@jalakai.co.uk>
  333. * @param string $group
  334. * @return bool
  335. */
  336. public function addGroup($group)
  337. {
  338. msg("authorisation method does not support independent group creation", -1);
  339. return false;
  340. }
  341. /**
  342. * Retrieve groups [implement only where required/possible]
  343. *
  344. * Set getGroups capability when implemented
  345. *
  346. * @author Chris Smith <chris@jalakai.co.uk>
  347. * @param int $start
  348. * @param int $limit
  349. * @return array
  350. */
  351. public function retrieveGroups($start = 0, $limit = 0)
  352. {
  353. msg("authorisation method does not support group list retrieval", -1);
  354. return array();
  355. }
  356. /**
  357. * Return case sensitivity of the backend [OPTIONAL]
  358. *
  359. * When your backend is caseinsensitive (eg. you can login with USER and
  360. * user) then you need to overwrite this method and return false
  361. *
  362. * @return bool
  363. */
  364. public function isCaseSensitive()
  365. {
  366. return true;
  367. }
  368. /**
  369. * Sanitize a given username [OPTIONAL]
  370. *
  371. * This function is applied to any user name that is given to
  372. * the backend and should also be applied to any user name within
  373. * the backend before returning it somewhere.
  374. *
  375. * This should be used to enforce username restrictions.
  376. *
  377. * @author Andreas Gohr <andi@splitbrain.org>
  378. * @param string $user username
  379. * @return string the cleaned username
  380. */
  381. public function cleanUser($user)
  382. {
  383. return $user;
  384. }
  385. /**
  386. * Sanitize a given groupname [OPTIONAL]
  387. *
  388. * This function is applied to any groupname that is given to
  389. * the backend and should also be applied to any groupname within
  390. * the backend before returning it somewhere.
  391. *
  392. * This should be used to enforce groupname restrictions.
  393. *
  394. * Groupnames are to be passed without a leading '@' here.
  395. *
  396. * @author Andreas Gohr <andi@splitbrain.org>
  397. * @param string $group groupname
  398. * @return string the cleaned groupname
  399. */
  400. public function cleanGroup($group)
  401. {
  402. return $group;
  403. }
  404. /**
  405. * Check Session Cache validity [implement only where required/possible]
  406. *
  407. * DokuWiki caches user info in the user's session for the timespan defined
  408. * in $conf['auth_security_timeout'].
  409. *
  410. * This makes sure slow authentication backends do not slow down DokuWiki.
  411. * This also means that changes to the user database will not be reflected
  412. * on currently logged in users.
  413. *
  414. * To accommodate for this, the user manager plugin will touch a reference
  415. * file whenever a change is submitted. This function compares the filetime
  416. * of this reference file with the time stored in the session.
  417. *
  418. * This reference file mechanism does not reflect changes done directly in
  419. * the backend's database through other means than the user manager plugin.
  420. *
  421. * Fast backends might want to return always false, to force rechecks on
  422. * each page load. Others might want to use their own checking here. If
  423. * unsure, do not override.
  424. *
  425. * @param string $user - The username
  426. * @author Andreas Gohr <andi@splitbrain.org>
  427. * @return bool
  428. */
  429. public function useSessionCache($user)
  430. {
  431. global $conf;
  432. return ($_SESSION[DOKU_COOKIE]['auth']['time'] >= @filemtime($conf['cachedir'] . '/sessionpurge'));
  433. }
  434. }