session.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bernhard Posselt <dev@bernhard-posselt.com>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  9. * @author Lukas Reschke <lukas@statuscode.ch>
  10. * @author Morris Jobke <hey@morrisjobke.de>
  11. * @author Robin Appelman <robin@icewind.nl>
  12. * @author Robin McCorkell <robin@mccorkell.me.uk>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Thomas Müller <thomas.mueller@tmit.eu>
  15. * @author Vincent Petry <pvince81@owncloud.com>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OC\User;
  33. use OC\Hooks\Emitter;
  34. use OCP\IUserSession;
  35. /**
  36. * Class Session
  37. *
  38. * Hooks available in scope \OC\User:
  39. * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
  40. * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
  41. * - preDelete(\OC\User\User $user)
  42. * - postDelete(\OC\User\User $user)
  43. * - preCreateUser(string $uid, string $password)
  44. * - postCreateUser(\OC\User\User $user)
  45. * - preLogin(string $user, string $password)
  46. * - postLogin(\OC\User\User $user, string $password)
  47. * - preRememberedLogin(string $uid)
  48. * - postRememberedLogin(\OC\User\User $user)
  49. * - logout()
  50. *
  51. * @package OC\User
  52. */
  53. class Session implements IUserSession, Emitter {
  54. /**
  55. * @var \OC\User\Manager $manager
  56. */
  57. private $manager;
  58. /**
  59. * @var \OC\Session\Session $session
  60. */
  61. private $session;
  62. /**
  63. * @var \OC\User\User $activeUser
  64. */
  65. protected $activeUser;
  66. /**
  67. * @param \OCP\IUserManager $manager
  68. * @param \OCP\ISession $session
  69. */
  70. public function __construct(\OCP\IUserManager $manager, \OCP\ISession $session) {
  71. $this->manager = $manager;
  72. $this->session = $session;
  73. }
  74. /**
  75. * @param string $scope
  76. * @param string $method
  77. * @param callable $callback
  78. */
  79. public function listen($scope, $method, callable $callback) {
  80. $this->manager->listen($scope, $method, $callback);
  81. }
  82. /**
  83. * @param string $scope optional
  84. * @param string $method optional
  85. * @param callable $callback optional
  86. */
  87. public function removeListener($scope = null, $method = null, callable $callback = null) {
  88. $this->manager->removeListener($scope, $method, $callback);
  89. }
  90. /**
  91. * get the manager object
  92. *
  93. * @return \OC\User\Manager
  94. */
  95. public function getManager() {
  96. return $this->manager;
  97. }
  98. /**
  99. * get the session object
  100. *
  101. * @return \OCP\ISession
  102. */
  103. public function getSession() {
  104. return $this->session;
  105. }
  106. /**
  107. * set the session object
  108. *
  109. * @param \OCP\ISession $session
  110. */
  111. public function setSession(\OCP\ISession $session) {
  112. if ($this->session instanceof \OCP\ISession) {
  113. $this->session->close();
  114. }
  115. $this->session = $session;
  116. $this->activeUser = null;
  117. }
  118. /**
  119. * set the currently active user
  120. *
  121. * @param \OC\User\User|null $user
  122. */
  123. public function setUser($user) {
  124. if (is_null($user)) {
  125. $this->session->remove('user_id');
  126. } else {
  127. $this->session->set('user_id', $user->getUID());
  128. }
  129. $this->activeUser = $user;
  130. }
  131. /**
  132. * get the current active user
  133. *
  134. * @return \OCP\IUser|null Current user, otherwise null
  135. */
  136. public function getUser() {
  137. // FIXME: This is a quick'n dirty work-around for the incognito mode as
  138. // described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
  139. if (\OC_User::isIncognitoMode()) {
  140. return null;
  141. }
  142. if ($this->activeUser) {
  143. return $this->activeUser;
  144. } else {
  145. $uid = $this->session->get('user_id');
  146. if ($uid !== null) {
  147. $this->activeUser = $this->manager->get($uid);
  148. return $this->activeUser;
  149. } else {
  150. return null;
  151. }
  152. }
  153. }
  154. /**
  155. * Checks whether the user is logged in
  156. *
  157. * @return bool if logged in
  158. */
  159. public function isLoggedIn() {
  160. return $this->getUser() !== null;
  161. }
  162. /**
  163. * set the login name
  164. *
  165. * @param string|null $loginName for the logged in user
  166. */
  167. public function setLoginName($loginName) {
  168. if (is_null($loginName)) {
  169. $this->session->remove('loginname');
  170. } else {
  171. $this->session->set('loginname', $loginName);
  172. }
  173. }
  174. /**
  175. * get the login name of the current user
  176. *
  177. * @return string
  178. */
  179. public function getLoginName() {
  180. if ($this->activeUser) {
  181. return $this->session->get('loginname');
  182. } else {
  183. $uid = $this->session->get('user_id');
  184. if ($uid) {
  185. $this->activeUser = $this->manager->get($uid);
  186. return $this->session->get('loginname');
  187. } else {
  188. return null;
  189. }
  190. }
  191. }
  192. /**
  193. * try to login with the provided credentials
  194. *
  195. * @param string $uid
  196. * @param string $password
  197. * @return boolean|null
  198. * @throws LoginException
  199. */
  200. public function login($uid, $password) {
  201. $this->session->regenerateId();
  202. $this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
  203. $user = $this->manager->checkPassword($uid, $password);
  204. if ($user !== false) {
  205. if (!is_null($user)) {
  206. if ($user->isEnabled()) {
  207. $this->setUser($user);
  208. $this->setLoginName($uid);
  209. $this->manager->emit('\OC\User', 'postLogin', array($user, $password));
  210. if ($this->isLoggedIn()) {
  211. return true;
  212. } else {
  213. throw new LoginException('Login canceled by app');
  214. }
  215. } else {
  216. return false;
  217. }
  218. }
  219. } else {
  220. return false;
  221. }
  222. }
  223. /**
  224. * perform login using the magic cookie (remember login)
  225. *
  226. * @param string $uid the username
  227. * @param string $currentToken
  228. * @return bool
  229. */
  230. public function loginWithCookie($uid, $currentToken) {
  231. $this->session->regenerateId();
  232. $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
  233. $user = $this->manager->get($uid);
  234. if (is_null($user)) {
  235. // user does not exist
  236. return false;
  237. }
  238. // get stored tokens
  239. $tokens = \OC::$server->getConfig()->getUserKeys($uid, 'login_token');
  240. // test cookies token against stored tokens
  241. if (!in_array($currentToken, $tokens, true)) {
  242. return false;
  243. }
  244. // replace successfully used token with a new one
  245. \OC::$server->getConfig()->deleteUserValue($uid, 'login_token', $currentToken);
  246. $newToken = \OC::$server->getSecureRandom()->generate(32);
  247. \OC::$server->getConfig()->setUserValue($uid, 'login_token', $newToken, time());
  248. $this->setMagicInCookie($user->getUID(), $newToken);
  249. //login
  250. $this->setUser($user);
  251. $this->manager->emit('\OC\User', 'postRememberedLogin', array($user));
  252. return true;
  253. }
  254. /**
  255. * logout the user from the session
  256. */
  257. public function logout() {
  258. $this->manager->emit('\OC\User', 'logout');
  259. $this->setUser(null);
  260. $this->setLoginName(null);
  261. $this->unsetMagicInCookie();
  262. $this->session->clear();
  263. }
  264. /**
  265. * Set cookie value to use in next page load
  266. *
  267. * @param string $username username to be set
  268. * @param string $token
  269. */
  270. public function setMagicInCookie($username, $token) {
  271. $secureCookie = \OC::$server->getRequest()->getServerProtocol() === 'https';
  272. $expires = time() + \OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
  273. setcookie("oc_username", $username, $expires, \OC::$WEBROOT, '', $secureCookie, true);
  274. setcookie("oc_token", $token, $expires, \OC::$WEBROOT, '', $secureCookie, true);
  275. setcookie("oc_remember_login", "1", $expires, \OC::$WEBROOT, '', $secureCookie, true);
  276. }
  277. /**
  278. * Remove cookie for "remember username"
  279. */
  280. public function unsetMagicInCookie() {
  281. //TODO: DI for cookies and IRequest
  282. $secureCookie = \OC::$server->getRequest()->getServerProtocol() === 'https';
  283. unset($_COOKIE["oc_username"]); //TODO: DI
  284. unset($_COOKIE["oc_token"]);
  285. unset($_COOKIE["oc_remember_login"]);
  286. setcookie('oc_username', '', time() - 3600, \OC::$WEBROOT, '',$secureCookie, true);
  287. setcookie('oc_token', '', time() - 3600, \OC::$WEBROOT, '', $secureCookie, true);
  288. setcookie('oc_remember_login', '', time() - 3600, \OC::$WEBROOT, '', $secureCookie, true);
  289. // old cookies might be stored under /webroot/ instead of /webroot
  290. // and Firefox doesn't like it!
  291. setcookie('oc_username', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
  292. setcookie('oc_token', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
  293. setcookie('oc_remember_login', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
  294. }
  295. }