PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/legacy/util/UserUtil.php

https://github.com/antoniom/core
PHP | 2009 lines | 1028 code | 267 blank | 714 comment | 374 complexity | 0ead410f6ad0525ddc0a349f33f8d529 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Copyright Zikula Foundation 2009 - Zikula Application Framework
  4. *
  5. * This work is contributed to the Zikula Foundation under one or more
  6. * Contributor Agreements and licensed to You under the following license:
  7. *
  8. * @license GNU/LGPv3 (or at your option any later version).
  9. * @package Util
  10. *
  11. * Please see the NOTICE file distributed with this source code for further
  12. * information regarding copyright and licensing.
  13. */
  14. use Zikula\Core\Event\GenericEvent;
  15. use UsersModule\Constants as UsersConstant;
  16. use Zikula\Framework\Exception\FatalException;
  17. use Zikula\Framework\Exception\RedirectException;
  18. use Zikula\Framework\Exception\ForbiddenException;
  19. /**
  20. * UserUtil
  21. */
  22. class UserUtil
  23. {
  24. /**
  25. * Cache for groups.
  26. *
  27. * @var array
  28. */
  29. protected static $groupCache = array();
  30. /**
  31. * Clear group cache.
  32. *
  33. * @return void
  34. */
  35. public function clearGroupCache()
  36. {
  37. self::$groupCache = array();
  38. }
  39. /**
  40. * Return a hash structure mapping uid to username.
  41. *
  42. * @param string $where The where clause to use (optional, default=array()).
  43. * @param string $orderBy The order by clause to use (optional, default=array()).
  44. * @param integer $limitOffset The select-limit offset (optional, default=null).
  45. * @param integer $limitNumRows The number of rows to fetch (optional, default=null).
  46. * @param string $assocKey The associative key to apply (optional) (default='uid').
  47. *
  48. * @deprecated since 1.3.0
  49. *
  50. * @return array An array mapping uid to username.
  51. */
  52. public static function getUsers($where = array(), $orderBy = array(), $limitOffset = null, $limitNumRows = null, $assocKey = 'uid')
  53. {
  54. $em = \ServiceUtil::get('doctrine')->getManager();
  55. $users = $em->getRepository('UsersModule\Entity\User')->findBy($where, $orderBy, $limitNumRows, $limitOffset);
  56. $items = array();
  57. foreach ($users as $user) {
  58. $items[$user[$assocKey]] = $user->toArray();
  59. }
  60. return $items;
  61. }
  62. /**
  63. * Return a group object.
  64. *
  65. * @param integer $gid The groupID to retrieve.
  66. *
  67. * @todo Decouple UserUtil and Groups?
  68. *
  69. * @return array The resulting group object.
  70. */
  71. public static function getGroup($gid)
  72. {
  73. return ModUtil::apiFunc('GroupsModule', 'user', 'get', array('gid' => $gid));
  74. }
  75. /**
  76. * Return a hash structure mapping gid to groupname.
  77. *
  78. * @param string $where The where clause to use (optional) (default=array()).
  79. * @param string $orderBy The order by clause to use (optional) (default=array()).
  80. * @param integer $limitOffset The select-limit offset (optional) (default=null).
  81. * @param integer $limitNumRows The number of rows to fetch (optional) (default=null).
  82. * @param string $assocKey The associative key to apply (optional) (default='gid').
  83. *
  84. * @return array An array mapping gid to groupname.
  85. */
  86. public static function getGroups($where = array(), $orderBy = array(), $limitOffset = null, $limitNumRows = null, $assocKey='gid')
  87. {
  88. $em = \ServiceUtil::get('doctrine')->getManager();
  89. $groups = $em->getRepository('GroupsModule\Entity\Group')->findBy($where, $orderBy, $limitNumRows, $limitOffset);
  90. $items = array();
  91. foreach ($groups as $group) {
  92. $items[$group[$assocKey]] = $group->toArray();
  93. }
  94. return $items;
  95. }
  96. /**
  97. * Return a (string) list of user-ids which can then be used in a SQL 'IN (...)' clause.
  98. *
  99. * @param string $where The where clause to use (optional).
  100. * @param string $orderBy The order by clause to use (optional).
  101. * @param string $separator The field separator to use (default=",") (optional).
  102. *
  103. * @return string A string list of user ids.
  104. */
  105. public static function getUserIdList($where = '', $orderBy = '', $separator = ',')
  106. {
  107. $userdata = self::getUsers($where, $orderBy);
  108. $list = '-1';
  109. if ($userdata && count($userdata)) {
  110. $uids = array_keys($userdata);
  111. sort($uids);
  112. $list = implode((string)$separator, $uids);
  113. }
  114. return $list;
  115. }
  116. /**
  117. * Return a (string) list of group-ids which can then be used in a SQL 'IN (...)' clause.
  118. *
  119. * @param string $where The where clause to use (optional).
  120. * @param string $orderBy The order by clause to use (optional).
  121. * @param string $separator The field separator to use (default=",") (optional).
  122. *
  123. * @return string A string list of group ids.
  124. */
  125. public static function getGroupIdList($where = array(), $orderBy = array(), $separator = ',')
  126. {
  127. $groupdata = self::getGroups($where, $orderBy);
  128. $list = '';
  129. if ($groupdata && count($groupdata)) {
  130. $gids = array_keys($groupdata);
  131. sort($gids);
  132. $list = implode((string)$separator, $gids);
  133. }
  134. return $list;
  135. }
  136. /**
  137. * Return an array group-ids for the specified user.
  138. *
  139. * @param integer $uid The user ID for which we want the groups.
  140. *
  141. * @return array An array of group IDs.
  142. */
  143. public static function getGroupsForUser($uid)
  144. {
  145. if (empty($uid)) {
  146. return array();
  147. }
  148. return ModUtil::apiFunc('GroupsModule', 'user', 'getusergroups', array('uid' => $uid, 'clean' => true));
  149. }
  150. /**
  151. * Return a string list of group-ids for the specified user.
  152. *
  153. * @param integer $uid The user ID for which we want the groups.
  154. * @param string $separator The field separator to use (default=",") (optional).
  155. *
  156. * @return string A string list of group ids.
  157. */
  158. public static function getGroupListForUser($uid = null, $separator = ',')
  159. {
  160. if (!$uid) {
  161. $uid = self::getVar('uid');
  162. }
  163. if (!$uid) {
  164. return '-1';
  165. }
  166. if (!isset(self::$groupCache[$uid])) {
  167. $gidArray = self::getGroupsForUser($uid);
  168. if ($gidArray && (bool)count($gidArray)) {
  169. sort($gidArray);
  170. self::$groupCache[$uid] = implode((string)$separator, $gidArray);
  171. } else {
  172. self::$groupCache[$uid] = '-1';
  173. }
  174. }
  175. return self::$groupCache[$uid];
  176. }
  177. /**
  178. * Return a string list of user-ids for the specified group.
  179. *
  180. * @param integer $gid The group ID for which we want the users.
  181. *
  182. * @return array An array of user IDs.
  183. */
  184. public static function getUsersForGroup($gid)
  185. {
  186. if (!$gid) {
  187. return array();
  188. }
  189. $group = ModUtil::apiFunc('GroupsModule', 'user', 'get', array('gid' => $gid));
  190. $members = $group['members'];
  191. $uids = array();
  192. foreach ($members as $uid => $membership) {
  193. $uids[] = $uid;
  194. }
  195. return $uids;
  196. }
  197. /**
  198. * Get a unique string for a user, depending on this group memberships.
  199. *
  200. * String ready to be used as part of the CacheID of the output views.
  201. * Useful when there aren't another user-based access privilegies, just group permissions.
  202. *
  203. * @param integer $uid User ID to get the group memberships from. Default: current user.
  204. *
  205. * @return string Cache GIDs string to use on Zikula_View.
  206. */
  207. public static function getGidCacheString($uid = null)
  208. {
  209. $str = UserUtil::getGroupListForUser($uid, '_');
  210. return $str == '-1' ? 'guest' : 'groups_'.$str;
  211. }
  212. /**
  213. * Get a unique string for a user, based on the uid.
  214. *
  215. * String ready to be used as part of the CacheID of the output views.
  216. * Useful for user-based access privilegies.
  217. *
  218. * @param integer $uid User ID to get string from. Default: current user.
  219. *
  220. * @return string Cache UID string to use on Zikula_View.
  221. */
  222. public static function getUidCacheString($uid = null)
  223. {
  224. $uid = $uid ? (int)$uid : self::getVar('uid');
  225. return !$uid ? 'guest' : 'uid_'.$uid;
  226. }
  227. /**
  228. * Return the defined dynamic user data fields.
  229. *
  230. * @return array An array of dynamic data field definitions.
  231. */
  232. public static function getDynamicDataFields()
  233. {
  234. // decide if we have to use the (obsolete) DUDs from the Profile module
  235. $profileModule = System::getVar('profilemodule', '');
  236. if (empty($profileModule) || $profileModule != 'Profile' || !ModUtil::available($profileModule)) {
  237. return array();
  238. }
  239. return DBUtil::selectObjectArray('user_property');
  240. }
  241. /**
  242. * Return a array structure for the user group selector.
  243. *
  244. * @param mixed $defaultValue The default value of the selector (default=0) (optional).
  245. * @param string $defaultText The text of the default value (optional).
  246. * @param array $ignore An array of keys to ignore (optional).
  247. * @param mixed $includeAll Whether to include an "All" choice (optional).
  248. * @param string $allText The text to display for the "All" choice (optional).
  249. *
  250. * @return array The array structure for the user group selector.
  251. */
  252. public static function getSelectorData_Group($defaultValue = 0, $defaultText = '', $ignore = array(), $includeAll = 0, $allText = '')
  253. {
  254. $dropdown = array();
  255. if ($defaultText) {
  256. $dropdown[] = array('id' => $defaultValue, 'name' => $defaultText);
  257. }
  258. $groupdata = self::getGroups(array(), array('name' => 'ASC'));
  259. if (!$groupdata || !count($groupdata)) {
  260. return $dropdown;
  261. }
  262. if ($includeAll) {
  263. $dropdown[] = array('id' => $includeAll, 'name' => $allText);
  264. }
  265. foreach (array_keys($groupdata) as $gid) {
  266. if (!isset($ignore[$gid])) {
  267. $gname = $groupdata[$gid]['name'];
  268. $dropdown[$gname] = array('id' => $gid, 'name' => $gname);
  269. }
  270. }
  271. ksort($dropdown);
  272. return $dropdown;
  273. }
  274. /**
  275. * Return a array strcuture for the user dropdown box.
  276. *
  277. * @param miexed $defaultValue The default value of the selector (optional) (default=0).
  278. * @param string $defaultText The text of the default value (optional) (default='').
  279. * @param array $ignore An array of keys to ignore (optional) (default=array()).
  280. * @param miexed $includeAll Whether to include an "All" choice (optional) (default=0).
  281. * @param string $allText The text to display for the "All" choice (optional) (default='').
  282. * @param string $exclude An SQL IN-LIST string to exclude specified uids.
  283. *
  284. * @return array The array structure for the user group selector.
  285. */
  286. public static function getSelectorData_User($defaultValue = 0, $defaultText = '', $ignore = array(), $includeAll = 0, $allText = '', $exclude = '')
  287. {
  288. $dropdown = array();
  289. if ($defaultText) {
  290. $dropdown[] = array('id' => $defaultValue, 'name' => $defaultText);
  291. }
  292. $where = '';
  293. if ($exclude) {
  294. $where = "WHERE uid NOT IN (" . DataUtil::formatForStore($exclude) . ")";
  295. }
  296. $userdata = self::getUsers($where, 'ORDER BY uname');
  297. if (!$userdata || !count($userdata)) {
  298. return $dropdown;
  299. }
  300. if ($includeAll) {
  301. $dropdown[] = array('id' => $includeAll, 'name' => $allText);
  302. }
  303. foreach (array_keys($userdata) as $uid) {
  304. if (!isset($ignore[$uid])) {
  305. $uname = $userdata[$uid]['uname'];
  306. $dropdown[$uname] = array('id' => $uid, 'name' => $uname);
  307. }
  308. }
  309. ksort($uname);
  310. return $dropdown;
  311. }
  312. /**
  313. * Retrieve the account recovery information for a user from the various authentication modules.
  314. *
  315. * @param numeric $uid The user id of the user for which account recovery information should be retrieved; optional, defaults to the
  316. * currently logged in user (an exception occurs if the current user is not logged in).
  317. *
  318. * @return array An array of account recovery information.
  319. *
  320. * @throws FatalException If the $uid parameter is not valid.
  321. */
  322. public static function getUserAccountRecoveryInfo($uid = -1)
  323. {
  324. if (!isset($uid) || !is_numeric($uid) || ((string)((int)$uid) != $uid) || (($uid < -1) || ($uid == 0) || ($uid == 1))) {
  325. throw new FatalException('Attempt to get authentication information for an invalid user id.');
  326. }
  327. if ($uid == -1) {
  328. if (self::isLoggedIn()) {
  329. $uid = self::getVar('uid');
  330. } else {
  331. throw new FatalException('Attempt to get authentication information for an invalid user id.');
  332. }
  333. }
  334. $userAuthenticationInfo = array();
  335. $authenticationModules = ModUtil::getModulesCapableOf(UsersConstant::CAPABILITY_AUTHENTICATION);
  336. if ($authenticationModules) {
  337. $accountRecoveryArgs = array (
  338. 'uid' => $uid,
  339. );
  340. foreach ($authenticationModules as $authenticationModule) {
  341. $moduleUserAuthenticationInfo = ModUtil::apiFunc($authenticationModule['name'], 'authentication', 'getAccountRecoveryInfoForUid', $accountRecoveryArgs, 'Zikula_Api_AbstractAuthentication');
  342. if (is_array($moduleUserAuthenticationInfo)) {
  343. $userAuthenticationInfo = array_merge($userAuthenticationInfo, $moduleUserAuthenticationInfo);
  344. }
  345. }
  346. }
  347. return $userAuthenticationInfo;
  348. }
  349. /**
  350. * Login.
  351. *
  352. * @param string $loginID Login Id.
  353. * @param string $userEnteredPassword The Password.
  354. * @param boolean $rememberme Whether or not to remember login.
  355. * @param boolean $checkPassword Whether or not to check the password.
  356. *
  357. * @return boolean
  358. */
  359. public static function login($loginID, $userEnteredPassword, $rememberme = false, $checkPassword = true)
  360. {
  361. LogUtil::log(__f('Warning! Function %1$s is deprecated. Please use %2$s instead.', array(__METHOD__, 'UserUtil::loginUsing()')), E_USER_DEPRECATED);
  362. $authenticationInfo = array(
  363. 'login_id' => $loginID,
  364. 'pass' => $userEnteredPassword,
  365. );
  366. $authenticationMethod = array(
  367. 'modname' => 'Users',
  368. );
  369. if (ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_LOGIN_METHOD, UsersConstant::DEFAULT_LOGIN_METHOD) == UsersConstant::LOGIN_METHOD_EMAIL) {
  370. $authenticationMethod['method'] = 'email';
  371. } else {
  372. $authenticationMethod['method'] = 'uname';
  373. }
  374. return self::loginUsing($authenticationMethod, $authenticationInfo, $rememberme, null, $checkPassword);
  375. }
  376. /**
  377. * Validation method previous authentication.
  378. *
  379. * @param array $authenticationMethod Auth method.
  380. * @param string $reentrantURL Reentrant URL (optional).
  381. *
  382. * @throws FatalException
  383. *
  384. * @return true
  385. */
  386. private static function preAuthenticationValidation(array $authenticationMethod, $reentrantURL = null)
  387. {
  388. if (empty($authenticationMethod) || (count($authenticationMethod) != 2)) {
  389. throw new FatalException(__f('An invalid %1$s parameter was received.', array('authenticationMethod')));
  390. }
  391. if (!isset($authenticationMethod['modname']) || !is_string($authenticationMethod['modname']) || empty($authenticationMethod['modname'])) {
  392. throw new FatalException(__f('An invalid %1$s parameter was received.', array('modname')));
  393. } elseif (!ModUtil::getInfoFromName($authenticationMethod['modname'])) {
  394. throw new FatalException(__f('The authentication module \'%1$s\' could not be found.', array($authenticationMethod['modname'])));
  395. } elseif (!ModUtil::available($authenticationMethod['modname'])) {
  396. throw new FatalException(__f('The authentication module \'%1$s\' is not available.', array($authenticationMethod['modname'])));
  397. } elseif (!ModUtil::loadApi($authenticationMethod['modname'], 'Authentication')) {
  398. throw new FatalException(__f('The authentication module \'%1$s\' could not be loaded.', array($authenticationMethod['modname'])));
  399. }
  400. if (!isset($authenticationMethod['method']) || !is_string($authenticationMethod['method']) || empty($authenticationMethod['method'])) {
  401. throw new FatalException(__f('An invalid %1$s parameter was received.', array('method')));
  402. } elseif (!ModUtil::apiFunc($authenticationMethod['modname'], 'Authentication', 'supportsAuthenticationMethod', array('method' => $authenticationMethod['method']), 'Zikula_Api_AbstractAuthentication')) {
  403. throw new FatalException(__f('The authentication method \'%1$s\' is not supported by the authentication module \'%2$s\'.', array($authenticationMethod['method'], $authenticationMethod['modname'])));
  404. }
  405. if (ModUtil::apiFunc($authenticationMethod['modname'], 'Authentication', 'isReentrant', null, 'Zikula_Api_AbstractAuthentication') && (!isset($reentrantURL) || empty($reentrantURL))) {
  406. throw new FatalException(__f('The authentication module \'%1$s\' is reentrant. A %2$s is required.', array($authenticationMethod['modname'], 'reentrantURL')));
  407. }
  408. return true;
  409. }
  410. /**
  411. * Authenticate a user's credentials against an authentication module, without any attempt to log the user in or look up a Zikula user account record.
  412. *
  413. * NOTE: Checking a password with an authentication method defined by the Users module is a special case.
  414. * The password is stored along with the account information, therefore the account information has to be
  415. * looked up by the checkPassword function in that module. Authentication modules other than the Users module should
  416. * make no attempt to look up account information,
  417. *
  418. * This function is used to check that a user is who he says he is without any attempt to log the user into the
  419. * Zikula system or look up his account information or status. It could be used, for example, to check the user's
  420. * credentials prior to registering with an authentication method like OpenID or Google Federated Login.
  421. *
  422. * This function differs from {@link authenticateUserUsing()} in that it does not make any attempt to look up a Zikula account
  423. * record for the user (nor should the authentication method specified).
  424. *
  425. * This function differs from {@link loginUsing()} in that it does not make any attempt to look up a Zikula account
  426. * record for the user (nor should the authentication method specified), and additionally it makes no attempt to log the user into
  427. * the Zikula system.
  428. *
  429. * ATTENTION: The authentication module function(s) called during this process may redirect the user to an external server
  430. * to perform authorization and/or authentication. The function calling checkPasswordUsing must already have anticipated
  431. * the reentrant nature of this process, must already have saved pertinent user state, must have supplied a
  432. * reentrant URL pointing to a function that will handle reentry into the login process silently, and must clear
  433. * any save user state immediately following the return of this function.
  434. *
  435. * @param array $authenticationMethod Authentication module and method name.
  436. * @param array $authenticationInfo Auth info array.
  437. * @param string $reentrantURL If the authentication module needs to redirect to an external authentication server (e.g., OpenID), then
  438. * this is the URL to return to in order to re-enter the log-in process. The pertinent user
  439. * state must have already been saved by the function calling checkPasswordUsing(), and the URL must
  440. * point to a Zikula_AbstractController function that is equipped to detect reentry, restore the
  441. * saved user state, and get the user back to the point where loginUsing is re-executed. This
  442. * is only optional if the authentication module identified by $authenticationMethod reports that it is not
  443. * reentrant (e.g., Users is guaranteed to not be reentrant).
  444. *
  445. * @return bool True if authentication info authenticates; otherwise false.
  446. */
  447. public static function checkPasswordUsing(array $authenticationMethod, array $authenticationInfo, $reentrantURL = null)
  448. {
  449. if (self::preAuthenticationValidation($authenticationMethod, $reentrantURL)) {
  450. // Authenticate the loginID and userEnteredPassword against the specified authentication module.
  451. // This should return the uid of the user logging in. Note that there are two routes here, both get a uid.
  452. $checkPasswordArgs = array(
  453. 'authentication_info' => $authenticationInfo,
  454. 'authentication_method' => $authenticationMethod,
  455. 'reentrant_url' => $reentrantURL,
  456. );
  457. return ModUtil::apiFunc($authenticationMethod['modname'], 'Authentication', 'checkPassword', $checkPasswordArgs, 'Zikula_Api_AbstractAuthentication');
  458. } else {
  459. return false;
  460. }
  461. }
  462. /**
  463. * Authenticate a user's credentials against an authentication module, without any attempt to log the user in.
  464. *
  465. * This function is used to check that a user is who he says he is, and that he has a valid user account with the
  466. * Zikula system. No attempt is made to log the user in to the Zikula system. It could be used, for example, to check
  467. * the user's credentials and Zikula system accoun status prior to performing a sensitive operation.
  468. *
  469. * This function differs from {@link checkPasswordUsing()} in that it attempts to look up a Zikula account
  470. * record for the user, and takes the user's account status into account when returning a value.
  471. *
  472. * This function differs from {@link loginUsing()} in that it makes no attempt to log the user into the Zikula system.
  473. *
  474. * ATTENTION: The authentication module function(s) called during this process may redirect the user to an external server
  475. * to perform authorization and/or authentication. The function calling authenticateUserUsing must already have anticipated
  476. * the reentrant nature of this process, must already have saved pertinent user state, must have supplied a
  477. * reentrant URL pointing to a function that will handle reentry into the login process silently, and must clear
  478. * any save user state immediately following the return of this function.
  479. *
  480. * @param array $authenticationMethod The name of the authentication module to use for authentication and the method name as defined by that module.
  481. * @param array $authenticationInfo The information needed by the authentication module for authentication, typically a loginID and pass.
  482. * @param string $reentrantURL If the authentication module needs to redirect to an external authentication server (e.g., OpenID), then
  483. * this is the URL to return to in order to re-enter the log-in process. The pertinent user
  484. * state must have already been saved by the function calling authenticateUserUsing(), and the URL must
  485. * point to a Zikula_AbstractController function that is equipped to detect reentry, restore the
  486. * saved user state, and get the user back to the point where loginUsing is re-executed. This
  487. * is only optional if the authentication module identified by $authenticationMethod reports that it is not
  488. * reentrant (e.g., Users is guaranteed to not be reentrant).
  489. *
  490. * @return mixed Zikula uid if the authentication info authenticates with the authentication module; otherwise false.
  491. */
  492. private static function internalAuthenticateUserUsing(array $authenticationMethod, array $authenticationInfo, $reentrantURL = null)
  493. {
  494. $authenticatedUid = false;
  495. if (self::preAuthenticationValidation($authenticationMethod, $reentrantURL)) {
  496. $authenticateUserArgs = array(
  497. 'authentication_info' => $authenticationInfo,
  498. 'authentication_method' => $authenticationMethod,
  499. 'reentrant_url' => $reentrantURL,
  500. );
  501. $authenticatedUid = ModUtil::apiFunc($authenticationMethod['modname'], 'Authentication', 'authenticateUser', $authenticateUserArgs, 'Zikula_Api_AbstractAuthentication');
  502. }
  503. return $authenticatedUid;
  504. }
  505. private static function internalUserAccountValidation($uid, $reportErrors = false, $userObj = false)
  506. {
  507. if (!$uid || !is_numeric($uid) || ((int)$uid != $uid)) {
  508. // We got something other than a uid from the authentication process.
  509. if (!LogUtil::hasErrors() && $reportErrors) {
  510. LogUtil::registerError(__('Sorry! Login failed. The information you provided was incorrect.'));
  511. }
  512. } else {
  513. if (!$userObj) {
  514. // Need to make sure the Users module stuff is loaded and available, especially if we are authenticating during
  515. // an upgrade or install.
  516. ModUtil::loadApi('Users', 'user', true);
  517. // The user's credentials have authenticated with the authentication module's method, but
  518. // now we have to check the account status itself. If the account status would not allow the
  519. // user to log in, then we return false.
  520. $userObj = self::getVars($uid);
  521. if (!$userObj) {
  522. // Might be a registration
  523. $userObj = self::getVars($uid, false, 'uid', true);
  524. }
  525. }
  526. if (!$userObj || !is_array($userObj)) {
  527. // Note that we have not actually logged into anything yet, just authenticated.
  528. throw new FatalException(__f('A %1$s (%2$s) was returned by the authenticating module, but a user account record (or registration request record) could not be found.', array('uid', $uid)));
  529. }
  530. if (!isset($userObj['activated'])) {
  531. // Provide a sane value.
  532. $userObj['activated'] = UsersConstant::ACTIVATED_INACTIVE;
  533. }
  534. if ($userObj['activated'] != UsersConstant::ACTIVATED_ACTIVE) {
  535. if ($reportErrors) {
  536. $displayVerifyPending = ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_LOGIN_DISPLAY_VERIFY_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_VERIFY_STATUS);
  537. $displayApprovalPending = ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_LOGIN_DISPLAY_APPROVAL_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_VERIFY_STATUS);
  538. if (($userObj['activated'] == UsersConstant::ACTIVATED_PENDING_REG) && ($displayApprovalPending || $displayVerifyPending)) {
  539. $moderationOrder = ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_REGISTRATION_APPROVAL_SEQUENCE, UsersConstant::DEFAULT_REGISTRATION_APPROVAL_SEQUENCE);
  540. if (!$userObj['isverified']
  541. && (($moderationOrder == UsersConstant::APPROVAL_AFTER) || ($moderationOrder == UsersConstant::APPROVAL_ANY)
  542. || (!empty($userObj['approved_by'])))
  543. && $displayVerifyPending
  544. ) {
  545. $message = __('Your request to register with this site is still waiting for verification of your e-mail address. Please check your inbox for a message from us.');
  546. } elseif (empty($userObj['approved_by'])
  547. && (($moderationOrder == UsersConstant::APPROVAL_BEFORE) || ($moderationOrder == UsersConstant::APPROVAL_ANY))
  548. && $displayApprovalPending
  549. ) {
  550. $message = __('Your request to register with this site is still waiting for approval from a site administrator.');
  551. }
  552. if (isset($message) && !empty($message)) {
  553. return LogUtil::registerError($message);
  554. }
  555. // It is a pending registration but the site admin elected to not display this to the user.
  556. // No exception here because the answer is simply "no." This will fall through to return false.
  557. } elseif (($userObj['activated'] == UsersConstant::ACTIVATED_INACTIVE) && ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_LOGIN_DISPLAY_INACTIVE_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_INACTIVE_STATUS)) {
  558. $message = __('Your account has been disabled. Please contact a site administrator for more information.');
  559. } elseif (($userObj['activated'] == UsersConstant::ACTIVATED_PENDING_DELETE) && ModUtil::getVar(UsersConstant::MODNAME, UsersConstant::MODVAR_LOGIN_DISPLAY_DELETE_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_DELETE_STATUS)) {
  560. $message = __('Your account has been disabled and is scheduled for removal. Please contact a site administrator for more information.');
  561. } else {
  562. $message = __('Sorry! Either there is no active user in our system with that information, or the information you provided does not match the information for your account.');
  563. }
  564. LogUtil::registerError($message);
  565. }
  566. $userObj = false;
  567. }
  568. }
  569. return $userObj;
  570. }
  571. /**
  572. * Authenticate a user's credentials against an authentication module, without any attempt to log the user in.
  573. *
  574. * This function is used to check that a user is who he says he is, and that he has a valid user account with the
  575. * Zikula system. No attempt is made to log the user in to the Zikula system. It could be used, for example, to check
  576. * the user's credentials and Zikula system accoun status prior to performing a sensitive operation.
  577. *
  578. * This function differs from {@link checkPasswordUsing()} in that it attempts to look up a Zikula account
  579. * record for the user, and takes the user's account status into account when returning a value.
  580. *
  581. * This function differs from {@link loginUsing()} in that it makes no attempt to log the user into the Zikula system.
  582. *
  583. * ATTENTION: The authentication module function(s) called during this process may redirect the user to an external server
  584. * to perform authorization and/or authentication. The function calling authenticateUserUsing must already have anticipated
  585. * the reentrant nature of this process, must already have saved pertinent user state, must have supplied a
  586. * reentrant URL pointing to a function that will handle reentry into the login process silently, and must clear
  587. * any save user state immediately following the return of this function.
  588. *
  589. * @param array $authenticationMethod The name of the authentication module to use for authentication and the method name as defined by that module.
  590. * @param array $authenticationInfo The information needed by the authentication module for authentication, typically a loginID and pass.
  591. * @param string $reentrantURL If the authentication module needs to redirect to an external authentication server (e.g., OpenID), then
  592. * this is the URL to return to in order to re-enter the log-in process. The pertinent user
  593. * state must have already been saved by the function calling authenticateUserUsing(), and the URL must
  594. * point to a Zikula_AbstractController function that is equipped to detect reentry, restore the
  595. * saved user state, and get the user back to the point where loginUsing is re-executed. This
  596. * is only optional if the authentication module identified by $authenticationMethod reports that it is not
  597. * reentrant (e.g., Users is guaranteed to not be reentrant).
  598. * @param boolean $reportErrors If true, then when validation of the account's ability to log in is performed, if errors are detected then
  599. * they will be reported through registering errors with Zikula's logging and error reporting system. If
  600. * false, then error reporting is supressed, and only the return value will indicate success or failure.
  601. *
  602. * @return array|bool The user account record of the user with the given credentials, if his credentials authenticate; otherwise false
  603. */
  604. public static function authenticateUserUsing(array $authenticationMethod, array $authenticationInfo, $reentrantURL = null, $reportErrors = false)
  605. {
  606. $userObj = false;
  607. $authenticatedUid = self::internalAuthenticateUserUsing($authenticationMethod, $authenticationInfo, $reentrantURL);
  608. if ($authenticatedUid) {
  609. $userObj = self::internalUserAccountValidation($authenticatedUid, $reportErrors);
  610. }
  611. return $userObj;
  612. }
  613. /**
  614. * Authenticate a user's credentials against an authentication module, logging him into the Zikula system.
  615. *
  616. * If the user is already logged in, then this function should behave as if {@link authenticateUserUsing()} was called.
  617. *
  618. * This function is used to check that a user is who he says he is, and that he has a valid user account with the
  619. * Zikula system. If so, the user is logged in to the Zikula system (if he is not already logged in). This function
  620. * should be used only to log a user into the Zikula system.
  621. *
  622. * This function differs from {@link checkPasswordUsing()} in that it attempts to look up a Zikula account
  623. * record for the user, and takes the user's account status into account when returning a value. Additionally,
  624. * the user is logged into the Zikula system if his credentials are verified with the authentication module specified.
  625. *
  626. * This function differs from {@link authenticateUserUsing()} in that it attempts to log the user into the Zikula system,
  627. * if he is not already logged in. If he is already logged in, then it should behave similarly to authenticateUserUsing().
  628. *
  629. * ATTENTION: The authentication module function(s) called during this process may redirect the user to an external server
  630. * to perform authorization and/or authentication. The function calling loginUsing must already have anticipated
  631. * the reentrant nature of this process, must already have saved pertinent user state, must have supplied a
  632. * reentrant URL pointing to a function that will handle reentry into the login process silently, and must clear
  633. * any save user state immediately following the return of this function.
  634. *
  635. * @param array $authenticationMethod Auth module name.
  636. * @param array $authenticationInfo Auth info array.
  637. * @param boolean $rememberMe Whether or not to remember login.
  638. * @param string $reentrantURL If the authentication module needs to redirect to an external authentication server (e.g., OpenID), then
  639. * this is the URL to return to in order to re-enter the log-in process. The pertinent user
  640. * state must have already been saved by the function calling loginUsing(), and the URL must
  641. * point to a Zikula_AbstractController function that is equipped to detect reentry, restore the
  642. * saved user state, and get the user back to the point where loginUsing is re-executed. This
  643. * is only optional if the authentication module identified by $authenticationMethod reports that it is not
  644. * reentrant (e.g., Users is guaranteed to not be reentrant), or if $checkPassword is false.
  645. * @param boolean $checkPassword Whether or not to check the password.
  646. * @param boolean $preauthenticatedUser Whether ot not is a preauthenticated user.
  647. *
  648. * @return array|bool The user account record of the user that has logged in successfully, otherwise false
  649. */
  650. public static function loginUsing(array $authenticationMethod, array $authenticationInfo, $rememberMe = false, $reentrantURL = null, $checkPassword = true, $preauthenticatedUser = null)
  651. {
  652. $userObj = false;
  653. if (self::preAuthenticationValidation($authenticationMethod, $authenticationInfo, $reentrantURL)) {
  654. // Authenticate the loginID and userEnteredPassword against the specified authentication module.
  655. // This should return the uid of the user logging in. Note that there are two routes here, both get a uid.
  656. // We do the authentication check first, before checking any account status information, because if the
  657. // person logging in cannot supply the proper credentials, then we should not show any detailed account status
  658. // to them. Instead they should just get the generic "no such user found or bad password" message.
  659. if ($checkPassword) {
  660. $authenticatedUid = self::internalAuthenticateUserUsing($authenticationMethod, $authenticationInfo, $reentrantURL, true);
  661. } elseif (isset($preauthenticatedUser)) {
  662. if (is_numeric($preauthenticatedUser)) {
  663. $authenticatedUid = $preauthenticatedUser;
  664. } elseif (is_array($preauthenticatedUser)) {
  665. $authenticatedUid = $preauthenticatedUser['uid'];
  666. $userObj = $preauthenticatedUser;
  667. } else {
  668. throw new FatalException();
  669. }
  670. } else {
  671. $authArgs = array(
  672. 'authentication_info' => $authenticationInfo,
  673. 'authentication_method' => $authenticationMethod,
  674. );
  675. $authenticatedUid = ModUtil::apiFunc($authenticationMethod['modname'], 'Authentication', 'getUidForAuththenticationInfo', $authArgs, 'Zikula_Api_AbstractAuthentication');
  676. }
  677. $session = ServiceUtil::get('request')->getSession();
  678. $userObj = self::internalUserAccountValidation($authenticatedUid, true, isset($userObj) ? $userObj : null);
  679. if ($userObj && is_array($userObj)) {
  680. // BEGIN ACTUAL LOGIN
  681. // Made it through all the checks. We can actually log in now.
  682. // Give any interested module one last chance to prevent the login from happening.
  683. $eventArgs = array(
  684. 'authentication_method' => $authenticationMethod,
  685. 'uid' => $userObj['uid'],
  686. );
  687. $event = new GenericEvent($userObj, $eventArgs);
  688. $event = EventUtil::dispatch('user.login.veto', $event);
  689. if ($event->isPropagationStopped()) {
  690. // The login attempt has been vetoed by one or more modules.
  691. $eventData = $event->getData();
  692. if (isset($eventData['retry']) && $eventData['retry']) {
  693. $sessionVarName = 'Users_Controller_User_login';
  694. $sessionNamespace = 'Zikula_Users';
  695. $redirectURL = ModUtil::url('Users', 'user', 'login', array('csrftoken' => SecurityUtil::generateCsrfToken()));
  696. } elseif (isset($eventData['redirect_func'])) {
  697. if (isset($eventData['redirect_func']['session'])) {
  698. $sessionVarName = $eventData['redirect_func']['session']['var'];
  699. $sessionNamespace = isset($eventData['redirect_func']['session']['namespace']) ? $eventData['redirect_func']['session']['namespace'] : '';
  700. }
  701. $redirectURL = ModUtil::url($eventData['redirect_func']['modname'], $eventData['redirect_func']['type'], $eventData['redirect_func']['func'], $eventData['redirect_func']['args']);
  702. }
  703. if (isset($redirectURL)) {
  704. if (isset($sessionVarName)) {
  705. SessionUtil::requireSession();
  706. $sessionVars = $session->get('users/Users_User_Controller_login', array());
  707. $sessionVars = array(
  708. 'returnpage' => isset($sessionVars['returnpage']) ? $sessionVars['returnpage'] : '',
  709. 'authentication_info' => $authenticationInfo,
  710. 'authentication_method' => $authenticationMethod,
  711. 'rememberme' => $rememberMe,
  712. 'user_obj' => $userObj,
  713. );
  714. $session->set("$sessionNamespace/$sessionVarName", $sessionVars);
  715. }
  716. $userObj = false;
  717. throw new RedirectException($redirectURL);
  718. } else {
  719. throw new ForbiddenException();
  720. }
  721. } else {
  722. // The login has not been vetoed
  723. // This is what really does the Zikula login
  724. self::setUserByUid($userObj['uid'], $rememberMe, $authenticationMethod);
  725. }
  726. }
  727. }
  728. return $userObj;
  729. }
  730. /**
  731. * Sets the currently logged in active user to the user account for the given Users module uname.
  732. *
  733. * No events are fired from this function. To receive events, use {@link loginUsing()}.
  734. *
  735. * @param string $uname The user name of the user who should be logged into the system; required.
  736. * @param boolean $rememberMe If the user's login should be maintained on the computer from which the user is logging in, set this to true;
  737. * optional, defaults to false.
  738. *
  739. * @return void
  740. */
  741. public static function setUserByUname($uname, $rememberMe = false)
  742. {
  743. if (!isset($uname) || !is_string($uname) || empty($uname)) {
  744. throw new FatalException(__('Attempt to set the current user with an invalid uname.'));
  745. }
  746. $uid = self::getIdFromName($uname);
  747. $authenticationMethod = array(
  748. 'modname' => 'Users',
  749. 'method' => 'uname',
  750. );
  751. self::setUserByUid($uid, $rememberMe, $authenticationMethod);
  752. }
  753. /**
  754. * Sets the currently logged in active user to the user account for the given uid.
  755. *
  756. * No events are fired from this function. To receive events, use {@link loginUsing()}.
  757. *
  758. * @param numeric $uid The user id of the user who should be logged into the system; required.
  759. * @param boolean $rememberMe If the user's login should be maintained on the computer from which the user is logging in, set this to true;
  760. * optional, defaults to false.
  761. * @param array $authenticationMethod An array containing the authentication method used to log the user in; optional,
  762. * defaults to the 'Users' module 'uname' method.
  763. *
  764. * @return void
  765. */
  766. public static function setUserByUid($uid, $rememberMe = false, array $authenticationMethod = null)
  767. {
  768. if (!isset($uid) || empty($uid) || ((string)((int)$uid) != $uid)) {
  769. throw new FatalException(__('Attempt to set the current user with an invalid uid.'));
  770. }
  771. $userObj = self::getVars($uid);
  772. if (!isset($userObj) || !is_array($userObj) || empty($userObj)) {
  773. throw new FatalException(__('Attempt to set the current user with an unknown uid.'));
  774. }
  775. if (!isset($authenticationMethod)) {
  776. $authenticationMethod = array(
  777. 'modname' => 'Users',
  778. 'method' => 'uname',
  779. );
  780. } elseif (empty($authenticationMethod) || !isset($authenticationMethod['modname']) || empty($authenticationMethod['modname'])
  781. || !isset($authenticationMethod['method']) || empty($authenticationMethod['method'])
  782. ) {
  783. throw new FatalException(__('Attempt to set the current user with an invalid authentication method.'));
  784. }
  785. // Storing Last Login date -- store it in UTC! Do not use date() function!
  786. $nowUTC = new DateTime(null, new DateTimeZone('UTC'));
  787. if (!self::setVar('lastlogin', $nowUTC->format('Y-m-d H:i:s'), $userObj['uid'])) {
  788. // show messages but continue
  789. LogUtil::registerError(__('Error! Could not save the log-in date.'));
  790. }
  791. if (!System::isInstalling()) {
  792. SessionUtil::requireSession();
  793. }
  794. $session = ServiceUtil::get('request')->getSession();
  795. // Set session variables -- this is what really does the Zikula login
  796. $session->set('uid', $userObj['uid']);
  797. $session->set('users/authentication_method', $authenticationMethod);
  798. if (!empty($rememberMe)) {
  799. $session->set('rememberme', 1);
  800. }
  801. // now that we've logged in the permissions previously calculated (if any) are invalid
  802. $GLOBALS['authinfogathered'][$userObj['uid']] = 0;
  803. }
  804. /**
  805. * Log the user out.
  806. *
  807. * @return bool true if the user successfully logged out, false otherwise
  808. */
  809. public static function logout()
  810. {
  811. if (self::isLoggedIn()) {
  812. $userObj = self::getVars(self::getVar('uid'));
  813. $session = ServiceUtil::get('request')->getSession();
  814. $authenticationMethod = $session->get('users/authentication_method', array('modname' => '', 'method' => ''));
  815. $session->invalidate();
  816. }
  817. return true;
  818. }
  819. /**
  820. * Is the user logged in?
  821. *
  822. * @return bool true if the user is logged in, false if they are not
  823. */
  824. public static function isLoggedIn()
  825. {
  826. return (bool)ServiceUtil::getManager()->get('request')->getSession()->get('uid');
  827. }
  828. /**
  829. * Counts how many times a user name has been used by user accounts in the system.
  830. *
  831. * @param string $uname The e-mail address in question (required).
  832. * @param int $excludeUid The uid to exclude from the check, used when checking modifications.
  833. *
  834. * @return integer|boolean The count, or false on error.
  835. */
  836. public static function getUnameUsageCount($uname, $excludeUid = 0)
  837. {
  838. if (!is_numeric($excludeUid) || ((int)$excludeUid != $excludeUid)) {
  839. return false;
  840. }
  841. $uname = DataUtil::formatForStore(mb_strtolower($uname));
  842. // get doctrine manager
  843. $em = \ServiceUtil::get('doctrine')->getManager();
  844. // count of uname appearances in users table
  845. $dql = "SELECT count(u.uid) FROM UsersModule\Entity\User u WHERE u.uname = '{$uname}'";
  846. if ($excludeUid > 1) {
  847. $dql .= " AND u.uid <> {$excludeUid}";
  848. }
  849. $query = $em->createQuery($dql);
  850. $ucount = $query->getSingleScalarResult();
  851. return (int)$ucount;
  852. }
  853. /**
  854. * Counts how many times an e-mail address has been used by user accounts in the system.
  855. *
  856. * @param string $emailAddress The e-mail address in question (required).
  857. * @param int $excludeUid The uid to exclude from the check, used when checking modifications.
  858. *
  859. * @return integer|boolean the count, or false on error.
  860. */
  861. public static function getEmailUsageCount($emailAddress, $excludeUid = 0)
  862. {
  863. if (!is_numeric($excludeUid) || ((int)$excludeUid != $excludeUid)) {
  864. return false;
  865. }
  866. $emailAddress = DataUtil::formatForStore(mb_strtolower($emailAddress));
  867. // get doctrine manager
  868. $em = \ServiceUtil::get('doctrine')->getManager();
  869. // count of email appearances in users table
  870. $dql = "SELECT COUNT(u.uid) FROM UsersModule\Entity\User u WHERE u.email = '{$emailAddress}'";
  871. if ($excludeUid > 1) {
  872. $dql .= " AND u.uid <> {$excludeUid}";
  873. }
  874. $query = $em->createQuery($dql);
  875. $ucount = (int)$query->getSingleScalarResult();
  876. // count of email appearances in users verification table
  877. $dql = "SELECT COUNT(v.id) FROM UsersModule\Entity\UserVerification v WHERE v.newemail = '{$emailAddress}' AND v.changetype = " . UsersCon

Large files files are truncated, but you can click here to view the full file