user.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2012 Frank Karlitschek frank@owncloud.org
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * This class provides wrapper methods for user management. Multiple backends are
  24. * supported. User management operations are delegated to the configured backend for
  25. * execution.
  26. *
  27. * Hooks provided:
  28. * pre_createUser(&run, uid, password)
  29. * post_createUser(uid, password)
  30. * pre_deleteUser(&run, uid)
  31. * post_deleteUser(uid)
  32. * pre_setPassword(&run, uid, password)
  33. * post_setPassword(uid, password)
  34. * pre_login(&run, uid)
  35. * post_login(uid)
  36. * logout()
  37. */
  38. class OC_User {
  39. // The backend used for user management
  40. private static $_usedBackends = array();
  41. private static $_setupedBackends = array();
  42. // Backends available (except database)
  43. private static $_backends = array();
  44. /**
  45. * @brief registers backend
  46. * @param $name name of the backend
  47. * @returns true/false
  48. *
  49. * Makes a list of backends that can be used by other modules
  50. */
  51. public static function registerBackend( $backend ) {
  52. self::$_backends[] = $backend;
  53. return true;
  54. }
  55. /**
  56. * @brief gets available backends
  57. * @returns array of backends
  58. *
  59. * Returns the names of all backends.
  60. */
  61. public static function getBackends() {
  62. return self::$_backends;
  63. }
  64. /**
  65. * @brief gets used backends
  66. * @returns array of backends
  67. *
  68. * Returns the names of all used backends.
  69. */
  70. public static function getUsedBackends() {
  71. return array_keys(self::$_usedBackends);
  72. }
  73. /**
  74. * @brief Adds the backend to the list of used backends
  75. * @param $backend default: database The backend to use for user managment
  76. * @returns true/false
  77. *
  78. * Set the User Authentication Module
  79. */
  80. public static function useBackend( $backend = 'database' ) {
  81. if($backend instanceof OC_User_Interface) {
  82. OC_Log::write('core', 'Adding user backend instance of '.get_class($backend).'.', OC_Log::DEBUG);
  83. self::$_usedBackends[get_class($backend)]=$backend;
  84. } else {
  85. // You'll never know what happens
  86. if( null === $backend OR !is_string( $backend )) {
  87. $backend = 'database';
  88. }
  89. // Load backend
  90. switch( $backend ) {
  91. case 'database':
  92. case 'mysql':
  93. case 'sqlite':
  94. OC_Log::write('core', 'Adding user backend '.$backend.'.', OC_Log::DEBUG);
  95. self::$_usedBackends[$backend] = new OC_User_Database();
  96. break;
  97. default:
  98. OC_Log::write('core', 'Adding default user backend '.$backend.'.', OC_Log::DEBUG);
  99. $className = 'OC_USER_' . strToUpper($backend);
  100. self::$_usedBackends[$backend] = new $className();
  101. break;
  102. }
  103. }
  104. return true;
  105. }
  106. /**
  107. * remove all used backends
  108. */
  109. public static function clearBackends() {
  110. self::$_usedBackends=array();
  111. }
  112. /**
  113. * setup the configured backends in config.php
  114. */
  115. public static function setupBackends() {
  116. $backends=OC_Config::getValue('user_backends', array());
  117. foreach($backends as $i=>$config) {
  118. $class=$config['class'];
  119. $arguments=$config['arguments'];
  120. if(class_exists($class)) {
  121. if(array_search($i, self::$_setupedBackends)===false) {
  122. // make a reflection object
  123. $reflectionObj = new ReflectionClass($class);
  124. // use Reflection to create a new instance, using the $args
  125. $backend = $reflectionObj->newInstanceArgs($arguments);
  126. self::useBackend($backend);
  127. $_setupedBackends[]=$i;
  128. } else {
  129. OC_Log::write('core', 'User backend '.$class.' already initialized.', OC_Log::DEBUG);
  130. }
  131. } else {
  132. OC_Log::write('core', 'User backend '.$class.' not found.', OC_Log::ERROR);
  133. }
  134. }
  135. }
  136. /**
  137. * @brief Create a new user
  138. * @param $uid The username of the user to create
  139. * @param $password The password of the new user
  140. * @returns true/false
  141. *
  142. * Creates a new user. Basic checking of username is done in OC_User
  143. * itself, not in its subclasses.
  144. *
  145. * Allowed characters in the username are: "a-z", "A-Z", "0-9" and "_.@-"
  146. */
  147. public static function createUser( $uid, $password ) {
  148. // Check the name for bad characters
  149. // Allowed are: "a-z", "A-Z", "0-9" and "_.@-"
  150. if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $uid )) {
  151. throw new Exception('Only the following characters are allowed in a username: "a-z", "A-Z", "0-9", and "_.@-"');
  152. }
  153. // No empty username
  154. if(trim($uid) == '') {
  155. throw new Exception('A valid username must be provided');
  156. }
  157. // No empty password
  158. if(trim($password) == '') {
  159. throw new Exception('A valid password must be provided');
  160. }
  161. // Check if user already exists
  162. if( self::userExists($uid) ) {
  163. throw new Exception('The username is already being used');
  164. }
  165. $run = true;
  166. OC_Hook::emit( "OC_User", "pre_createUser", array( "run" => &$run, "uid" => $uid, "password" => $password ));
  167. if( $run ) {
  168. //create the user in the first backend that supports creating users
  169. foreach(self::$_usedBackends as $backend) {
  170. if(!$backend->implementsActions(OC_USER_BACKEND_CREATE_USER))
  171. continue;
  172. $backend->createUser($uid, $password);
  173. OC_Hook::emit( "OC_User", "post_createUser", array( "uid" => $uid, "password" => $password ));
  174. return self::userExists($uid);
  175. }
  176. }
  177. return false;
  178. }
  179. /**
  180. * @brief delete a user
  181. * @param $uid The username of the user to delete
  182. * @returns true/false
  183. *
  184. * Deletes a user
  185. */
  186. public static function deleteUser( $uid ) {
  187. $run = true;
  188. OC_Hook::emit( "OC_User", "pre_deleteUser", array( "run" => &$run, "uid" => $uid ));
  189. if( $run ) {
  190. //delete the user from all backends
  191. foreach(self::$_usedBackends as $backend) {
  192. $backend->deleteUser($uid);
  193. }
  194. if (self::userExists($uid)) {
  195. return false;
  196. }
  197. // We have to delete the user from all groups
  198. foreach( OC_Group::getUserGroups( $uid ) as $i ) {
  199. OC_Group::removeFromGroup( $uid, $i );
  200. }
  201. // Delete the user's keys in preferences
  202. OC_Preferences::deleteUser($uid);
  203. // Delete user files in /data/
  204. OC_Helper::rmdirr(OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ) . '/'.$uid.'/');
  205. // Emit and exit
  206. OC_Hook::emit( "OC_User", "post_deleteUser", array( "uid" => $uid ));
  207. return true;
  208. }
  209. else{
  210. return false;
  211. }
  212. }
  213. /**
  214. * @brief Try to login a user
  215. * @param $uid The username of the user to log in
  216. * @param $password The password of the user
  217. * @returns true/false
  218. *
  219. * Log in a user and regenerate a new session - if the password is ok
  220. */
  221. public static function login( $uid, $password ) {
  222. $run = true;
  223. OC_Hook::emit( "OC_User", "pre_login", array( "run" => &$run, "uid" => $uid ));
  224. if( $run ) {
  225. $uid = self::checkPassword( $uid, $password );
  226. $enabled = self::isEnabled($uid);
  227. if($uid && $enabled) {
  228. session_regenerate_id(true);
  229. self::setUserId($uid);
  230. OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password ));
  231. return true;
  232. }
  233. }
  234. return false;
  235. }
  236. /**
  237. * @brief Sets user id for session and triggers emit
  238. * @returns true
  239. *
  240. */
  241. public static function setUserId($uid) {
  242. $_SESSION['user_id'] = $uid;
  243. return true;
  244. }
  245. /**
  246. * @brief Logs the current user out and kills all the session data
  247. * @returns true
  248. *
  249. * Logout, destroys session
  250. */
  251. public static function logout() {
  252. OC_Hook::emit( "OC_User", "logout", array());
  253. session_unset();
  254. session_destroy();
  255. OC_User::unsetMagicInCookie();
  256. return true;
  257. }
  258. /**
  259. * @brief Check if the user is logged in
  260. * @returns true/false
  261. *
  262. * Checks if the user is logged in
  263. */
  264. public static function isLoggedIn() {
  265. if( isset($_SESSION['user_id']) AND $_SESSION['user_id']) {
  266. OC_App::loadApps(array('authentication'));
  267. self::setupBackends();
  268. if (self::userExists($_SESSION['user_id']) ) {
  269. return true;
  270. }
  271. }
  272. return false;
  273. }
  274. /**
  275. * @brief get the user id of the user currently logged in.
  276. * @return string uid or false
  277. */
  278. public static function getUser() {
  279. if( isset($_SESSION['user_id']) AND $_SESSION['user_id'] ) {
  280. return $_SESSION['user_id'];
  281. }
  282. else{
  283. return false;
  284. }
  285. }
  286. /**
  287. * @brief Autogenerate a password
  288. * @returns string
  289. *
  290. * generates a password
  291. */
  292. public static function generatePassword() {
  293. return uniqId();
  294. }
  295. /**
  296. * @brief Set password
  297. * @param $uid The username
  298. * @param $password The new password
  299. * @returns true/false
  300. *
  301. * Change the password of a user
  302. */
  303. public static function setPassword( $uid, $password ) {
  304. $run = true;
  305. OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password ));
  306. if( $run ) {
  307. $success = false;
  308. foreach(self::$_usedBackends as $backend) {
  309. if($backend->implementsActions(OC_USER_BACKEND_SET_PASSWORD)) {
  310. if($backend->userExists($uid)) {
  311. $success |= $backend->setPassword($uid, $password);
  312. }
  313. }
  314. }
  315. // invalidate all login cookies
  316. OC_Preferences::deleteApp($uid, 'login_token');
  317. OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password ));
  318. return $success;
  319. }
  320. else{
  321. return false;
  322. }
  323. }
  324. /**
  325. * @brief Check if the password is correct
  326. * @param $uid The username
  327. * @param $password The password
  328. * @returns string
  329. *
  330. * Check if the password is correct without logging in the user
  331. * returns the user id or false
  332. */
  333. public static function checkPassword( $uid, $password ) {
  334. foreach(self::$_usedBackends as $backend) {
  335. if($backend->implementsActions(OC_USER_BACKEND_CHECK_PASSWORD)) {
  336. $result=$backend->checkPassword( $uid, $password );
  337. if($result) {
  338. return $result;
  339. }
  340. }
  341. }
  342. }
  343. /**
  344. * @brief Check if the password is correct
  345. * @param $uid The username
  346. * @param $password The password
  347. * @returns string
  348. *
  349. * returns the path to the users home directory
  350. */
  351. public static function getHome($uid) {
  352. foreach(self::$_usedBackends as $backend) {
  353. if($backend->implementsActions(OC_USER_BACKEND_GET_HOME)) {
  354. $result=$backend->getHome($uid);
  355. if($result) {
  356. return $result;
  357. }
  358. }
  359. }
  360. return OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ) . '/' . $uid;
  361. }
  362. /**
  363. * @brief Get a list of all users
  364. * @returns array with all uids
  365. *
  366. * Get a list of all users.
  367. */
  368. public static function getUsers($search = '', $limit = null, $offset = null) {
  369. $users = array();
  370. foreach (self::$_usedBackends as $backend) {
  371. $backendUsers = $backend->getUsers($search, $limit, $offset);
  372. if (is_array($backendUsers)) {
  373. $users = array_merge($users, $backendUsers);
  374. }
  375. }
  376. asort($users);
  377. return $users;
  378. }
  379. /**
  380. * @brief check if a user exists
  381. * @param string $uid the username
  382. * @param string $excludingBackend (default none)
  383. * @return boolean
  384. */
  385. public static function userExists($uid, $excludingBackend=null) {
  386. foreach(self::$_usedBackends as $backend) {
  387. if (!is_null($excludingBackend) && !strcmp(get_class($backend),$excludingBackend)) {
  388. OC_Log::write('OC_User', $excludingBackend . 'excluded from user existance check.', OC_Log::DEBUG);
  389. continue;
  390. }
  391. $result=$backend->userExists($uid);
  392. if($result===true) {
  393. return true;
  394. }
  395. }
  396. return false;
  397. }
  398. /**
  399. * disables a user
  400. * @param string $userid the user to disable
  401. */
  402. public static function disableUser($userid) {
  403. $sql = "INSERT INTO `*PREFIX*preferences` (`userid`, `appid`, `configkey`, `configvalue`) VALUES(?, ?, ?, ?)";
  404. $stmt = OC_DB::prepare($sql);
  405. if ( ! OC_DB::isError($stmt) ) {
  406. $result = $stmt->execute(array($userid, 'core', 'enabled', 'false'));
  407. if ( OC_DB::isError($result) ) {
  408. OC_Log::write('OC_User', 'could not enable user: '. OC_DB::getErrorMessage($result), OC_Log::ERROR);
  409. }
  410. } else {
  411. OC_Log::write('OC_User', 'could not disable user: '. OC_DB::getErrorMessage($stmt), OC_Log::ERROR);
  412. }
  413. }
  414. /**
  415. * enable a user
  416. * @param string $userid
  417. */
  418. public static function enableUser($userid) {
  419. $sql = "DELETE FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? AND `configvalue` = ?";
  420. $stmt = OC_DB::prepare($sql);
  421. if ( ! OC_DB::isError($stmt) ) {
  422. $result = $stmt->execute(array($userid, 'core', 'enabled', 'false'));
  423. if ( OC_DB::isError($result) ) {
  424. OC_Log::write('OC_User', 'could not enable user: '. OC_DB::getErrorMessage($result), OC_Log::ERROR);
  425. }
  426. } else {
  427. OC_Log::write('OC_User', 'could not enable user: '. OC_DB::getErrorMessage($stmt), OC_Log::ERROR);
  428. }
  429. }
  430. /**
  431. * checks if a user is enabled
  432. * @param string $userid
  433. * @return bool
  434. */
  435. public static function isEnabled($userid) {
  436. $sql = "SELECT `userid` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? AND `configvalue` = ?";
  437. $stmt = OC_DB::prepare($sql);
  438. if ( ! OC_DB::isError($stmt) ) {
  439. $result = $stmt->execute(array($userid, 'core', 'enabled', 'false'));
  440. if ( ! OC_DB::isError($result) ) {
  441. return $result->numRows() ? false : true;
  442. } else {
  443. OC_Log::write('OC_User', 'could not check if enabled: '. OC_DB::getErrorMessage($result), OC_Log::ERROR);
  444. }
  445. } else {
  446. OC_Log::write('OC_User', 'could not check if enabled: '. OC_DB::getErrorMessage($stmt), OC_Log::ERROR);
  447. }
  448. return false;
  449. }
  450. /**
  451. * @brief Set cookie value to use in next page load
  452. * @param string $username username to be set
  453. */
  454. public static function setMagicInCookie($username, $token) {
  455. $secure_cookie = OC_Config::getValue("forcessl", false);
  456. $expires = time() + OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15);
  457. setcookie("oc_username", $username, $expires, '', '', $secure_cookie);
  458. setcookie("oc_token", $token, $expires, '', '', $secure_cookie, true);
  459. setcookie("oc_remember_login", true, $expires, '', '', $secure_cookie);
  460. }
  461. /**
  462. * @brief Remove cookie for "remember username"
  463. */
  464. public static function unsetMagicInCookie() {
  465. unset($_COOKIE["oc_username"]);
  466. unset($_COOKIE["oc_token"]);
  467. unset($_COOKIE["oc_remember_login"]);
  468. setcookie("oc_username", null, -1);
  469. setcookie("oc_token", null, -1);
  470. setcookie("oc_remember_login", null, -1);
  471. }
  472. }