You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

460 lines
15 KiB

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