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.

294 lines
7.4 KiB

11 years ago
  1. <?php
  2. /**
  3. * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace OC\User;
  9. use OC\Hooks\PublicEmitter;
  10. use OCP\IUserManager;
  11. /**
  12. * Class Manager
  13. *
  14. * Hooks available in scope \OC\User:
  15. * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
  16. * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
  17. * - preDelete(\OC\User\User $user)
  18. * - postDelete(\OC\User\User $user)
  19. * - preCreateUser(string $uid, string $password)
  20. * - postCreateUser(\OC\User\User $user, string $password)
  21. *
  22. * @package OC\User
  23. */
  24. class Manager extends PublicEmitter implements IUserManager {
  25. /**
  26. * @var \OC_User_Interface[] $backends
  27. */
  28. private $backends = array();
  29. /**
  30. * @var \OC\User\User[] $cachedUsers
  31. */
  32. private $cachedUsers = array();
  33. /**
  34. * @var \OC\AllConfig $config
  35. */
  36. private $config;
  37. /**
  38. * @param \OC\AllConfig $config
  39. */
  40. public function __construct($config = null) {
  41. $this->config = $config;
  42. $cachedUsers = $this->cachedUsers;
  43. $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
  44. $i = array_search($user, $cachedUsers);
  45. if ($i !== false) {
  46. unset($cachedUsers[$i]);
  47. }
  48. });
  49. $this->listen('\OC\User', 'postLogin', function ($user) {
  50. $user->updateLastLoginTimestamp();
  51. });
  52. $this->listen('\OC\User', 'postRememberedLogin', function ($user) {
  53. $user->updateLastLoginTimestamp();
  54. });
  55. }
  56. /**
  57. * register a user backend
  58. *
  59. * @param \OC_User_Interface $backend
  60. */
  61. public function registerBackend($backend) {
  62. $this->backends[] = $backend;
  63. }
  64. /**
  65. * remove a user backend
  66. *
  67. * @param \OC_User_Interface $backend
  68. */
  69. public function removeBackend($backend) {
  70. $this->cachedUsers = array();
  71. if (($i = array_search($backend, $this->backends)) !== false) {
  72. unset($this->backends[$i]);
  73. }
  74. }
  75. /**
  76. * remove all user backends
  77. */
  78. public function clearBackends() {
  79. $this->cachedUsers = array();
  80. $this->backends = array();
  81. }
  82. /**
  83. * get a user by user id
  84. *
  85. * @param string $uid
  86. * @return \OC\User\User
  87. */
  88. public function get($uid) {
  89. if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
  90. return $this->cachedUsers[$uid];
  91. }
  92. foreach ($this->backends as $backend) {
  93. if ($backend->userExists($uid)) {
  94. return $this->getUserObject($uid, $backend);
  95. }
  96. }
  97. return null;
  98. }
  99. /**
  100. * get or construct the user object
  101. *
  102. * @param string $uid
  103. * @param \OC_User_Interface $backend
  104. * @return \OC\User\User
  105. */
  106. protected function getUserObject($uid, $backend) {
  107. if (isset($this->cachedUsers[$uid])) {
  108. return $this->cachedUsers[$uid];
  109. }
  110. $this->cachedUsers[$uid] = new User($uid, $backend, $this, $this->config);
  111. return $this->cachedUsers[$uid];
  112. }
  113. /**
  114. * check if a user exists
  115. *
  116. * @param string $uid
  117. * @return bool
  118. */
  119. public function userExists($uid) {
  120. $user = $this->get($uid);
  121. return ($user !== null);
  122. }
  123. /**
  124. * remove deleted user from cache
  125. *
  126. * @param string $uid
  127. * @return bool
  128. */
  129. public function delete($uid) {
  130. if (isset($this->cachedUsers[$uid])) {
  131. unset($this->cachedUsers[$uid]);
  132. return true;
  133. }
  134. return false;
  135. }
  136. /**
  137. * Check if the password is valid for the user
  138. *
  139. * @param string $loginname
  140. * @param string $password
  141. * @return mixed the User object on success, false otherwise
  142. */
  143. public function checkPassword($loginname, $password) {
  144. foreach ($this->backends as $backend) {
  145. if ($backend->implementsActions(\OC_USER_BACKEND_CHECK_PASSWORD)) {
  146. $uid = $backend->checkPassword($loginname, $password);
  147. if ($uid !== false) {
  148. return $this->getUserObject($uid, $backend);
  149. }
  150. }
  151. }
  152. $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
  153. $forwardedFor = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '';
  154. \OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. $remoteAddr .'\', X-Forwarded-For: \''. $forwardedFor .'\')', array('app' => 'core'));
  155. return false;
  156. }
  157. /**
  158. * search by user id
  159. *
  160. * @param string $pattern
  161. * @param int $limit
  162. * @param int $offset
  163. * @return \OC\User\User[]
  164. */
  165. public function search($pattern, $limit = null, $offset = null) {
  166. $users = array();
  167. foreach ($this->backends as $backend) {
  168. $backendUsers = $backend->getUsers($pattern, $limit, $offset);
  169. if (is_array($backendUsers)) {
  170. foreach ($backendUsers as $uid) {
  171. $users[$uid] = $this->getUserObject($uid, $backend);
  172. }
  173. }
  174. }
  175. uasort($users, function ($a, $b) {
  176. /**
  177. * @var \OC\User\User $a
  178. * @var \OC\User\User $b
  179. */
  180. return strcmp($a->getUID(), $b->getUID());
  181. });
  182. return $users;
  183. }
  184. /**
  185. * search by displayName
  186. *
  187. * @param string $pattern
  188. * @param int $limit
  189. * @param int $offset
  190. * @return \OC\User\User[]
  191. */
  192. public function searchDisplayName($pattern, $limit = null, $offset = null) {
  193. $users = array();
  194. foreach ($this->backends as $backend) {
  195. $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
  196. if (is_array($backendUsers)) {
  197. foreach ($backendUsers as $uid => $displayName) {
  198. $users[] = $this->getUserObject($uid, $backend);
  199. }
  200. }
  201. }
  202. usort($users, function ($a, $b) {
  203. /**
  204. * @var \OC\User\User $a
  205. * @var \OC\User\User $b
  206. */
  207. return strcmp($a->getDisplayName(), $b->getDisplayName());
  208. });
  209. return $users;
  210. }
  211. /**
  212. * @param string $uid
  213. * @param string $password
  214. * @throws \Exception
  215. * @return bool|\OC\User\User the created user of false
  216. */
  217. public function createUser($uid, $password) {
  218. $l = \OC::$server->getL10N('lib');
  219. // Check the name for bad characters
  220. // Allowed are: "a-z", "A-Z", "0-9" and "_.@-"
  221. if (preg_match('/[^a-zA-Z0-9 _\.@\-]/', $uid)) {
  222. throw new \Exception($l->t('Only the following characters are allowed in a username:'
  223. . ' "a-z", "A-Z", "0-9", and "_.@-"'));
  224. }
  225. // No empty username
  226. if (trim($uid) == '') {
  227. throw new \Exception($l->t('A valid username must be provided'));
  228. }
  229. // No empty password
  230. if (trim($password) == '') {
  231. throw new \Exception($l->t('A valid password must be provided'));
  232. }
  233. // Check if user already exists
  234. if ($this->userExists($uid)) {
  235. throw new \Exception($l->t('The username is already being used'));
  236. }
  237. $this->emit('\OC\User', 'preCreateUser', array($uid, $password));
  238. foreach ($this->backends as $backend) {
  239. if ($backend->implementsActions(\OC_USER_BACKEND_CREATE_USER)) {
  240. $backend->createUser($uid, $password);
  241. $user = $this->getUserObject($uid, $backend);
  242. $this->emit('\OC\User', 'postCreateUser', array($user, $password));
  243. return $user;
  244. }
  245. }
  246. return false;
  247. }
  248. /**
  249. * returns how many users per backend exist (if supported by backend)
  250. *
  251. * @return array an array of backend class as key and count number as value
  252. */
  253. public function countUsers() {
  254. $userCountStatistics = array();
  255. foreach ($this->backends as $backend) {
  256. if ($backend->implementsActions(\OC_USER_BACKEND_COUNT_USERS)) {
  257. $backendusers = $backend->countUsers();
  258. if($backendusers !== false) {
  259. if(isset($userCountStatistics[get_class($backend)])) {
  260. $userCountStatistics[get_class($backend)] += $backendusers;
  261. } else {
  262. $userCountStatistics[get_class($backend)] = $backendusers;
  263. }
  264. }
  265. }
  266. }
  267. return $userCountStatistics;
  268. }
  269. }