OC_User.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. use OC\Authentication\Token\IProvider;
  8. use OC\User\LoginException;
  9. use OCP\Authentication\Exceptions\InvalidTokenException;
  10. use OCP\Authentication\Exceptions\WipeTokenException;
  11. use OCP\Authentication\Token\IToken;
  12. use OCP\EventDispatcher\IEventDispatcher;
  13. use OCP\IGroupManager;
  14. use OCP\ISession;
  15. use OCP\IUser;
  16. use OCP\IUserManager;
  17. use OCP\Server;
  18. use OCP\Session\Exceptions\SessionNotAvailableException;
  19. use OCP\User\Events\BeforeUserLoggedInEvent;
  20. use OCP\User\Events\UserLoggedInEvent;
  21. use Psr\Log\LoggerInterface;
  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. * Note that &run is deprecated and won't work anymore.
  28. *
  29. * Hooks provided:
  30. * pre_createUser(&run, uid, password)
  31. * post_createUser(uid, password)
  32. * pre_deleteUser(&run, uid)
  33. * post_deleteUser(uid)
  34. * pre_setPassword(&run, uid, password, recoveryPassword)
  35. * post_setPassword(uid, password, recoveryPassword)
  36. * pre_login(&run, uid, password)
  37. * post_login(uid)
  38. * logout()
  39. */
  40. class OC_User {
  41. private static $_usedBackends = [];
  42. private static $_setupedBackends = [];
  43. // bool, stores if a user want to access a resource anonymously, e.g if they open a public link
  44. private static $incognitoMode = false;
  45. /**
  46. * Adds the backend to the list of used backends
  47. *
  48. * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
  49. * @return bool
  50. *
  51. * Set the User Authentication Module
  52. * @suppress PhanDeprecatedFunction
  53. */
  54. public static function useBackend($backend = 'database') {
  55. if ($backend instanceof \OCP\UserInterface) {
  56. self::$_usedBackends[get_class($backend)] = $backend;
  57. \OC::$server->getUserManager()->registerBackend($backend);
  58. } else {
  59. // You'll never know what happens
  60. if ($backend === null or !is_string($backend)) {
  61. $backend = 'database';
  62. }
  63. // Load backend
  64. switch ($backend) {
  65. case 'database':
  66. case 'mysql':
  67. case 'sqlite':
  68. Server::get(LoggerInterface::class)->debug('Adding user backend ' . $backend . '.', ['app' => 'core']);
  69. self::$_usedBackends[$backend] = new \OC\User\Database();
  70. \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
  71. break;
  72. case 'dummy':
  73. self::$_usedBackends[$backend] = new \Test\Util\User\Dummy();
  74. \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
  75. break;
  76. default:
  77. Server::get(LoggerInterface::class)->debug('Adding default user backend ' . $backend . '.', ['app' => 'core']);
  78. $className = 'OC_USER_' . strtoupper($backend);
  79. self::$_usedBackends[$backend] = new $className();
  80. \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
  81. break;
  82. }
  83. }
  84. return true;
  85. }
  86. /**
  87. * remove all used backends
  88. */
  89. public static function clearBackends() {
  90. self::$_usedBackends = [];
  91. \OC::$server->getUserManager()->clearBackends();
  92. }
  93. /**
  94. * setup the configured backends in config.php
  95. * @suppress PhanDeprecatedFunction
  96. */
  97. public static function setupBackends() {
  98. OC_App::loadApps(['prelogin']);
  99. $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
  100. if (isset($backends['default']) && !$backends['default']) {
  101. // clear default backends
  102. self::clearBackends();
  103. }
  104. foreach ($backends as $i => $config) {
  105. if (!is_array($config)) {
  106. continue;
  107. }
  108. $class = $config['class'];
  109. $arguments = $config['arguments'];
  110. if (class_exists($class)) {
  111. if (!in_array($i, self::$_setupedBackends)) {
  112. // make a reflection object
  113. $reflectionObj = new ReflectionClass($class);
  114. // use Reflection to create a new instance, using the $args
  115. $backend = $reflectionObj->newInstanceArgs($arguments);
  116. self::useBackend($backend);
  117. self::$_setupedBackends[] = $i;
  118. } else {
  119. Server::get(LoggerInterface::class)->debug('User backend ' . $class . ' already initialized.', ['app' => 'core']);
  120. }
  121. } else {
  122. Server::get(LoggerInterface::class)->error('User backend ' . $class . ' not found.', ['app' => 'core']);
  123. }
  124. }
  125. }
  126. /**
  127. * Try to login a user, assuming authentication
  128. * has already happened (e.g. via Single Sign On).
  129. *
  130. * Log in a user and regenerate a new session.
  131. *
  132. * @param \OCP\Authentication\IApacheBackend $backend
  133. * @return bool
  134. */
  135. public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
  136. $uid = $backend->getCurrentUserId();
  137. $run = true;
  138. OC_Hook::emit("OC_User", "pre_login", ["run" => &$run, "uid" => $uid, 'backend' => $backend]);
  139. if ($uid) {
  140. if (self::getUser() !== $uid) {
  141. self::setUserId($uid);
  142. $userSession = \OC::$server->getUserSession();
  143. /** @var IEventDispatcher $dispatcher */
  144. $dispatcher = \OC::$server->get(IEventDispatcher::class);
  145. if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) {
  146. $message = \OC::$server->getL10N('lib')->t('Account disabled');
  147. throw new LoginException($message);
  148. }
  149. $userSession->setLoginName($uid);
  150. $request = OC::$server->getRequest();
  151. $password = null;
  152. if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) {
  153. $password = $backend->getCurrentUserSecret();
  154. }
  155. /** @var IEventDispatcher $dispatcher */
  156. $dispatcher->dispatchTyped(new BeforeUserLoggedInEvent($uid, $password, $backend));
  157. $userSession->createSessionToken($request, $uid, $uid, $password);
  158. $userSession->createRememberMeToken($userSession->getUser());
  159. if (empty($password)) {
  160. $tokenProvider = \OC::$server->get(IProvider::class);
  161. try {
  162. $token = $tokenProvider->getToken($userSession->getSession()->getId());
  163. $token->setScope([
  164. IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true,
  165. IToken::SCOPE_FILESYSTEM => true,
  166. ]);
  167. $tokenProvider->updateToken($token);
  168. } catch (InvalidTokenException|WipeTokenException|SessionNotAvailableException) {
  169. // swallow the exceptions as we do not deal with them here
  170. // simply skip updating the token when is it missing
  171. }
  172. }
  173. // setup the filesystem
  174. OC_Util::setupFS($uid);
  175. // first call the post_login hooks, the login-process needs to be
  176. // completed before we can safely create the users folder.
  177. // For example encryption needs to initialize the users keys first
  178. // before we can create the user folder with the skeleton files
  179. OC_Hook::emit(
  180. 'OC_User',
  181. 'post_login',
  182. [
  183. 'uid' => $uid,
  184. 'password' => $password,
  185. 'isTokenLogin' => false,
  186. ]
  187. );
  188. $dispatcher->dispatchTyped(new UserLoggedInEvent(
  189. \OC::$server->get(IUserManager::class)->get($uid),
  190. $uid,
  191. null,
  192. false)
  193. );
  194. //trigger creation of user home and /files folder
  195. \OC::$server->getUserFolder($uid);
  196. }
  197. return true;
  198. }
  199. return false;
  200. }
  201. /**
  202. * Verify with Apache whether user is authenticated.
  203. *
  204. * @return boolean|null
  205. * true: authenticated
  206. * false: not authenticated
  207. * null: not handled / no backend available
  208. */
  209. public static function handleApacheAuth() {
  210. $backend = self::findFirstActiveUsedBackend();
  211. if ($backend) {
  212. OC_App::loadApps();
  213. //setup extra user backends
  214. self::setupBackends();
  215. \OC::$server->getUserSession()->unsetMagicInCookie();
  216. return self::loginWithApache($backend);
  217. }
  218. return null;
  219. }
  220. /**
  221. * Sets user id for session and triggers emit
  222. *
  223. * @param string $uid
  224. */
  225. public static function setUserId($uid) {
  226. $userSession = \OC::$server->getUserSession();
  227. $userManager = \OC::$server->getUserManager();
  228. if ($user = $userManager->get($uid)) {
  229. $userSession->setUser($user);
  230. } else {
  231. \OC::$server->getSession()->set('user_id', $uid);
  232. }
  233. }
  234. /**
  235. * Check if the user is logged in, considers also the HTTP basic credentials
  236. *
  237. * @deprecated use \OC::$server->getUserSession()->isLoggedIn()
  238. * @return bool
  239. */
  240. public static function isLoggedIn() {
  241. return \OC::$server->getUserSession()->isLoggedIn();
  242. }
  243. /**
  244. * set incognito mode, e.g. if a user wants to open a public link
  245. *
  246. * @param bool $status
  247. */
  248. public static function setIncognitoMode($status) {
  249. self::$incognitoMode = $status;
  250. }
  251. /**
  252. * get incognito mode status
  253. *
  254. * @return bool
  255. */
  256. public static function isIncognitoMode() {
  257. return self::$incognitoMode;
  258. }
  259. /**
  260. * Returns the current logout URL valid for the currently logged-in user
  261. *
  262. * @param \OCP\IURLGenerator $urlGenerator
  263. * @return string
  264. */
  265. public static function getLogoutUrl(\OCP\IURLGenerator $urlGenerator) {
  266. $backend = self::findFirstActiveUsedBackend();
  267. if ($backend) {
  268. return $backend->getLogoutUrl();
  269. }
  270. $user = \OC::$server->getUserSession()->getUser();
  271. if ($user instanceof IUser) {
  272. $backend = $user->getBackend();
  273. if ($backend instanceof \OCP\User\Backend\ICustomLogout) {
  274. return $backend->getLogoutUrl();
  275. }
  276. }
  277. $logoutUrl = $urlGenerator->linkToRoute('core.login.logout');
  278. $logoutUrl .= '?requesttoken=' . urlencode(\OCP\Util::callRegister());
  279. return $logoutUrl;
  280. }
  281. /**
  282. * Check if the user is an admin user
  283. *
  284. * @param string $uid uid of the admin
  285. * @return bool
  286. */
  287. public static function isAdminUser($uid) {
  288. $user = Server::get(IUserManager::class)->get($uid);
  289. $isAdmin = $user && Server::get(IGroupManager::class)->isAdmin($user->getUID());
  290. return $isAdmin && self::$incognitoMode === false;
  291. }
  292. /**
  293. * get the user id of the user currently logged in.
  294. *
  295. * @return string|false uid or false
  296. */
  297. public static function getUser() {
  298. $uid = Server::get(ISession::class)?->get('user_id');
  299. if (!is_null($uid) && self::$incognitoMode === false) {
  300. return $uid;
  301. } else {
  302. return false;
  303. }
  304. }
  305. /**
  306. * Set password
  307. *
  308. * @param string $uid The username
  309. * @param string $password The new password
  310. * @param string $recoveryPassword for the encryption app to reset encryption keys
  311. * @return bool
  312. *
  313. * Change the password of a user
  314. */
  315. public static function setPassword($uid, $password, $recoveryPassword = null) {
  316. $user = \OC::$server->getUserManager()->get($uid);
  317. if ($user) {
  318. return $user->setPassword($password, $recoveryPassword);
  319. } else {
  320. return false;
  321. }
  322. }
  323. /**
  324. * @param string $uid The username
  325. * @return string
  326. *
  327. * returns the path to the users home directory
  328. * @deprecated Use \OC::$server->getUserManager->getHome()
  329. */
  330. public static function getHome($uid) {
  331. $user = \OC::$server->getUserManager()->get($uid);
  332. if ($user) {
  333. return $user->getHome();
  334. } else {
  335. return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
  336. }
  337. }
  338. /**
  339. * Get a list of all users display name
  340. *
  341. * @param string $search
  342. * @param int $limit
  343. * @param int $offset
  344. * @return array associative array with all display names (value) and corresponding uids (key)
  345. *
  346. * Get a list of all display names and user ids.
  347. * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
  348. */
  349. public static function getDisplayNames($search = '', $limit = null, $offset = null) {
  350. $displayNames = [];
  351. $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset);
  352. foreach ($users as $user) {
  353. $displayNames[$user->getUID()] = $user->getDisplayName();
  354. }
  355. return $displayNames;
  356. }
  357. /**
  358. * Returns the first active backend from self::$_usedBackends.
  359. *
  360. * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
  361. */
  362. private static function findFirstActiveUsedBackend() {
  363. foreach (self::$_usedBackends as $backend) {
  364. if ($backend instanceof OCP\Authentication\IApacheBackend) {
  365. if ($backend->isSessionActive()) {
  366. return $backend;
  367. }
  368. }
  369. }
  370. return null;
  371. }
  372. }