PageRenderTime 26ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/core/Admin.php

https://gitlab.com/michield/phpList
PHP | 416 lines | 285 code | 39 blank | 92 comment | 21 complexity | 4fc4a80161dcae76493a528a75958aac MD5 | raw file
  1. <?php
  2. namespace phpList;
  3. use phpList\helper\String;
  4. use phpList\helper\Util;
  5. use phpList\Entity\AdminEntity;
  6. class Admin
  7. {
  8. public $id = 0;
  9. private $loginname;
  10. public function __construct( $adminModel, $pass )
  11. {
  12. $this->adminModel = $adminModel;
  13. $this->pass = $pass;
  14. }
  15. /**
  16. * Set the login name and return false if it alreay is in use
  17. * @param string $loginname
  18. * @return bool
  19. */
  20. public function setLoginname($loginname)
  21. {
  22. $this->loginname = strtolower(String::normalize($loginname));
  23. return $this->isLoginUnique();
  24. }
  25. private $namelc;
  26. /**
  27. * @param string $namelc
  28. */
  29. public function setNamelc($namelc)
  30. {
  31. $this->namelc = strtolower(String::normalize($namelc));
  32. }
  33. public $email;
  34. public $created;
  35. public $modified;
  36. public $modifiedby;
  37. private $password;
  38. /**
  39. * @param string $password
  40. * Will encrypt and set the password
  41. * and update db when changing the password of
  42. * an existing admin
  43. */
  44. public function setPassword($password)
  45. {
  46. $this->password = Util::encryptPass($password);
  47. if ($this->id != 0) {
  48. phpList::DB()->query(
  49. sprintf(
  50. 'UPDATE %s
  51. SET password = "%s", passwordchanged = CURRENT_TIMESTAMP
  52. WHERE id = %s',
  53. Config::getTableName('admin'),
  54. $this->password,
  55. $this->id
  56. )
  57. );
  58. }
  59. }
  60. public $passwordchanged;
  61. public $superuser;
  62. public $disabled;
  63. /**
  64. * @var array ('subscribers' => true/false, 'campaigns' => true/false,'statistics' => true/false, 'settings' => true/false);
  65. */
  66. public $privileges;
  67. /**
  68. * Get an admin by id
  69. * @param int $id
  70. * @return Admin
  71. */
  72. public static function getAdmin($id)
  73. {
  74. $result = phpList::DB()->query(
  75. sprintf(
  76. 'SELECT * FROM %s
  77. WHERE id = %d',
  78. Config::getTableName('admin'),
  79. $id
  80. )
  81. );
  82. return Admin::adminFromArray($result->fetch(\PDO::FETCH_ASSOC));
  83. }
  84. /**
  85. * Get all admins from the database
  86. * If a string is provided, it will try to search for the admins matching the string
  87. * @param string $search
  88. * @return array Admin
  89. */
  90. public static function getAdmins($search = '')
  91. {
  92. $admins = array();
  93. $condition = '';
  94. if ($search != '') {
  95. $search = String::sqlEscape($search);
  96. $condition = sprintf(' WHERE loginname LIKE "%%%s%%" OR email LIKE "%%%s%%"', $search, $search);
  97. }
  98. $result = phpList::DB()->query(
  99. sprintf(
  100. 'SELECT * FROM %s
  101. %s ORDER BY loginname',
  102. Config::getTableName('admin'),
  103. $condition
  104. )
  105. );
  106. while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
  107. $admins[] = Admin::adminFromArray($row);
  108. }
  109. return $admins;
  110. }
  111. /**
  112. * Save the admin in the database
  113. */
  114. public function save()
  115. {
  116. if ($this->id != 0) {
  117. //TODO: maybe not send empty modifiedby param?
  118. $this->update('call to save function');
  119. } else {
  120. if (!$this->isLoginUnique()) {
  121. throw new \Exception('Login name already in use');
  122. } elseif (empty($this->namelc)) {
  123. //TODO: why is this used?
  124. $this->namelc = $this->loginname;
  125. }
  126. phpList::DB()->query(
  127. sprintf(
  128. 'INSERT INTO %s (loginname, namelc, created)
  129. VALUES("%s", "%s", CURRENT_TIMESTAMP)',
  130. Config::getTableName('admin'),
  131. $this->loginname,
  132. $this->namelc
  133. )
  134. );
  135. }
  136. }
  137. /**
  138. * Update back to db
  139. * $modifiedby can be any string to see who has changed the record
  140. * @param string $modifiedby
  141. */
  142. public function update($modifiedby)
  143. {
  144. $privileges = String::sqlEscape(serialize($this->privileges));
  145. phpList::DB()->query(
  146. sprintf(
  147. 'UPDATE %s SET
  148. loginname = "%s", namelc = "%s", email = "%s", modified = CURRENT_TIMESTAMP, modifiedby = "%s", superuser = %d, disabled = %d, privileges = "%s"',
  149. Config::getTableName('admin'),
  150. $this->loginname,
  151. $this->namelc,
  152. $this->email,
  153. $modifiedby,
  154. $this->superuser,
  155. $this->disabled,
  156. $privileges
  157. )
  158. );
  159. }
  160. /**
  161. * Remove an admin from the database
  162. * @param int $id
  163. */
  164. //TODO: not sure if this should be static
  165. public static function delete($id)
  166. {
  167. $tables = array(
  168. Config::getTableName('admin') => 'id',
  169. Config::getTableName('admin_attribute') => 'adminid',
  170. Config::getTableName('admin_task') => 'adminid'
  171. );
  172. phpList::DB()->deleteFromArray($tables, $id);
  173. }
  174. /**
  175. * Check if the login name is unique
  176. */
  177. private function isLoginUnique()
  178. {
  179. $condition = '';
  180. if ($this->id != 0) {
  181. $condition = ' AND NOT id = ' . $this->id;
  182. }
  183. $result = phpList::DB()->query(
  184. sprintf(
  185. 'SELECT COUNT(id) FROM %s
  186. WHERE loginname = "%s" %s',
  187. Config::getTableName('admin'),
  188. $this->loginname,
  189. $condition
  190. )
  191. );
  192. return ($result->fetchColumn(0) == 0) ? true : false;
  193. }
  194. /**
  195. * Add admin attributes to the database
  196. * @param array $attributes
  197. */
  198. public function addAttributes($attributes)
  199. {
  200. while (list($key, $val) = each($attributes)) {
  201. phpList::DB()->query(
  202. sprintf(
  203. 'REPLACE INTO %s
  204. (adminid,adminattributeid,value)
  205. VALUES(%d,%d,"%s")',
  206. Config::getTableName('admin_attribute'),
  207. $this->id,
  208. $key,
  209. addslashes($val)
  210. )
  211. );
  212. }
  213. }
  214. //TODO: Should we still use admin attributes?
  215. public function getAttributes()
  216. {
  217. $attributes = array();
  218. $result = phpList::DB()->query(
  219. sprintf(
  220. 'SELECT * FROM %s AS adm_att
  221. INNER JOIN %s AS adm
  222. ON adm_att.adminid = adm.id
  223. WHERE adm.id = %d',
  224. Config::getTableName('admin_attribute'),
  225. Config::getTableName('admin'),
  226. $this->id
  227. )
  228. );
  229. while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
  230. $attributes[] = $row;
  231. }
  232. return $attributes;
  233. }
  234. /**
  235. * Will check login credentials and return result array
  236. * @param $login string
  237. * @param $password string
  238. * @return array $result Keys: admin [object], error [string], result [bool]
  239. */
  240. function validateLogin( $plainPass, $username )
  241. {
  242. $return = array(
  243. 'result' => false,
  244. // FIXME: Re-add translation / formatting logic to message
  245. 'error' => 'Login failed',
  246. 'admin' => null
  247. );
  248. // Hash the supplied password for comparison
  249. $encPass = $this->pass->encrypt( $plainPass );
  250. $result = $this->adminModel->getAdminByUsername( $username );
  251. // If an admin was found with that username
  252. if( $result ) {
  253. $adminEntity = $this->adminEntityFromArray( $result );
  254. }
  255. /*
  256. * TODO: this should not happen imo, can this be removed
  257. #Password encryption verification.
  258. if(strlen($passwordDB)<$GLOBALS['hash_length']) { // Passwords are encrypted but the actual is not.
  259. #Encrypt the actual DB password before performing the validation below.
  260. $encryptedPassDB = phpList::encryptPass($passwordDB);
  261. $query = "update %s set password = '%s' where loginname = ?";
  262. $query = sprintf($query, $GLOBALS['tables']['admin'], $encryptedPassDB);
  263. $passwordDB = $encryptedPassDB;
  264. $result = Sql_Query_Params($query, array($login));
  265. }*/
  266. if ( ! $result ) {
  267. $return['error'] = 'Admin not found'; // If admin not found
  268. } elseif( $adminEntity->disabled ) {
  269. // FIXME: translation / formatting via s() removed
  270. $return['error'] = 'Your account has been disabled';
  271. } elseif ( $encPass == $adminEntity->encPass ) {
  272. $return['result'] = true;
  273. $return['error'] = '';
  274. $return['admin'] = $adminEntity;
  275. } elseif ( $encPass != $adminEntity->encPass ) {
  276. // FIXME: translation / formatting via s() removed
  277. $return['error'] = 'Incorrect password';
  278. } else {
  279. // FIXME: translation / formatting via s() removed
  280. $return['error'] = 'Unknown error';
  281. }
  282. return $return;
  283. }
  284. private static function adminEntityFromArray( array $array )
  285. {
  286. // FIXME: Move this object instantiation to DI.
  287. $adminEntity = new AdminEntity( $array['loginname'] );
  288. $adminEntity->id = $array['id'];
  289. $adminEntity->namelc = $array['namelc'];
  290. $adminEntity->email = $array['email'];
  291. $adminEntity->created = $array['created'];
  292. $adminEntity->modified = $array['modified'];
  293. $adminEntity->modifiedby = $array['modifiedby'];
  294. $adminEntity->encPass = $array['password'];
  295. $adminEntity->passwordchanged = $array['passwordchanged'];
  296. $adminEntity->superuser = $array['superuser'];
  297. $adminEntity->disabled = $array['disabled'];
  298. $adminEntity->privileges = unserialize( $array['privileges'] );
  299. return $adminEntity;
  300. }
  301. /**
  302. * Send email with a random encrypted token.
  303. * @return bool
  304. */
  305. public function sendPasswordToken()
  306. {
  307. #Check if the token is not present in the database yet.
  308. //TODO: make key_value a unique field in the database
  309. do {
  310. $unique_key = md5(uniqid(mt_rand()));
  311. } while (!phpList::DB()->query(
  312. sprintf(
  313. 'INSERT INTO %s (date, admin, key_value)
  314. VALUES (CURRENT_TIMESTAMP, %d, "%s")',
  315. Config::getTableName('admin_password_request'),
  316. $this->id,
  317. $unique_key
  318. )
  319. ));
  320. $urlroot = Config::get('website') . Config::get('adminpages');
  321. #Build the email body to be sent, and finally send it.
  322. $emailBody = s('Hello') . ' ' . $this->loginname . "\n\n";
  323. $emailBody .= s('You have requested a new password for phpList.') . "\n\n";
  324. $emailBody .= s('To enter a new one, please visit the following link:') . "\n\n";
  325. $emailBody .= sprintf('http://%s/?page=login&token=%s', $urlroot, $unique_key) . "\n\n";
  326. $emailBody .= s('You have 24 hours left to change your password. After that, your token won\'t be valid.');
  327. //TODO: convert to new mail class
  328. return phpListMailer::sendMail($this->email, s('New password'), "\n\n" . $emailBody, '', '', true);
  329. }
  330. /**
  331. * Delete expired tokens from the database
  332. */
  333. public static function deleteOldTokens()
  334. {
  335. phpList::DB()->query(
  336. sprintf(
  337. 'DELETE FROM %s
  338. WHERE date_add( date, INTERVAL %s) < CURRENT_TIMESTAMP',
  339. Config::getTableName('admin_password_request'),
  340. Config::get('password_change_timeframe')
  341. ),
  342. 1
  343. );
  344. phpList::DB()->query(
  345. sprintf(
  346. 'DELETE FROM %s
  347. WHERE expires < CURRENT_TIMESTAMP',
  348. Config::getTableName('admintoken')
  349. ),
  350. 1
  351. );
  352. }
  353. /**
  354. * Check if a form token is valid
  355. * @param string $token
  356. * @return bool
  357. */
  358. public function verifyToken($token)
  359. {
  360. if (empty($token)) {
  361. return false;
  362. }
  363. ## @@@TODO for now ignore the error. This will cause a block on editing admins if the table doesn't exist.
  364. $result = phpList::DB()->prepare(
  365. sprintf(
  366. 'SELECT id FROM %s
  367. WHERE adminid = %d
  368. AND value = :token
  369. AND expires > CURRENT_TIMESTAMP',
  370. Config::getTableName('admintoken'),
  371. $this->id
  372. )
  373. );
  374. $result->execute(array(':token' => $token));
  375. return ($result->rowCount() > 0);
  376. }
  377. }