users.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /**
  3. * User model implementation linking PHPActiveRecord to the Security class.
  4. *
  5. * @author Taddeus Kroes
  6. * @date 06-10-2012
  7. * @since 0.2
  8. */
  9. namespace webbasics;
  10. require_once 'security.php';
  11. require_once 'php-activerecord/ActiveRecord.php';
  12. abstract class ActiveRecordUser extends \ActiveRecord\Model implements SecureUser, RoleUser {
  13. /**
  14. * Name of the cookie holding user data.
  15. * @var string
  16. */
  17. const COOKIE_NAME = 'auth_userdata';
  18. /**
  19. * Keep authentication cookies for one month.
  20. * @var int
  21. */
  22. const COOKIE_EXPIRE = 2592000;
  23. /**
  24. * Length of the salt prepended to the password.
  25. * @var int
  26. */
  27. const SALT_LENGTH = 6;
  28. /**
  29. * Name of the table users are saved in.
  30. * @var string
  31. */
  32. static $table_name = 'users';
  33. /**
  34. *
  35. * @var string[5]
  36. */
  37. static $attr_protected = array('username', 'password_hash', 'salt', 'session_hash', 'role');
  38. /**
  39. * Plain password, optional and used only during registration process.
  40. * @var string
  41. */
  42. public $password;
  43. /**
  44. *
  45. */
  46. function before_create() {
  47. $this->hashPassword();
  48. $this->saltPasswordHash();
  49. }
  50. /**
  51. *
  52. */
  53. function hashPassword() {
  54. if (!$this->password_hash && $this->password)
  55. $this->password_hash = Security::hash($this->password);
  56. }
  57. /**
  58. *
  59. */
  60. function saltPasswordHash() {
  61. $this->password_hash = Security::hash(self::generateSalt() . $this->password_hash);
  62. }
  63. /**
  64. * @see BaseUser::getUsername()
  65. */
  66. function getUsername() {
  67. return $this->username;
  68. }
  69. /**
  70. * @see BaseUser::getPasswordHash()
  71. */
  72. function getPasswordHash() {
  73. return $this->password_hash;
  74. }
  75. /**
  76. * @see SecureUser::getSessionHash()
  77. */
  78. function getSessionHash() {
  79. return $this->session_hash;
  80. }
  81. /**
  82. * @see SecureUser::setSessionHash()
  83. */
  84. function setSessionHash($hash) {
  85. $this->update_attribute('session_hash', $hash);
  86. }
  87. /**
  88. *
  89. *
  90. * @return string A 6-byte salt.
  91. */
  92. private static function generateSalt() {
  93. $class_name = get_called_class();
  94. return Security::generateRandomString($class_name::SALT_LENGTH);
  95. }
  96. /**
  97. * @see SecureUser::getRole()
  98. */
  99. function getRole() {
  100. return $this->role;
  101. }
  102. function saveCookie() {
  103. $class_name = get_class($this);
  104. $data = array($this->getUsername(), $this->getSessionHash());
  105. setcookie($class_name::COOKIE_NAME, implode(',', $data), $class_name::COOKIE_EXPIRE);
  106. }
  107. /**
  108. * @see
  109. */
  110. static function loadFromCookie() {
  111. $class_name = get_called_class();
  112. if (!isset($_COOKIE[$class_name::COOKIE_NAME]))
  113. return false;
  114. list($username, $session_hash) = explode(',', $_COOKIE[$class_name::COOKIE_NAME]);
  115. $user = $class_name::first(array(
  116. 'conditions' => compact('username', 'session_hash')
  117. ));
  118. Security::getInstance()->loginUser($user);
  119. $user->saveCookie();
  120. return true;
  121. }
  122. }
  123. abstract class RegisteredUser extends ActiveRecordUser {
  124. /**
  125. * Length of passwords generated after registration.
  126. * @var int
  127. */
  128. const PASSWORD_LENGTH = 8;
  129. /**
  130. * Attributes protected against mass assignment.
  131. * @var string[6]
  132. */
  133. static $attr_protected = array('username', 'password_hash', 'salt',
  134. 'session_hash', 'role', 'registration_token');
  135. /**
  136. * Send a confirmation e-mail after registration.
  137. * @var string[1]
  138. */
  139. static $after_create = array('composeConfirmationMail');
  140. function before_create() {
  141. parent::before_create();
  142. $this->registration_token = sha1($this->getUsername() . time());
  143. }
  144. static function confirmRegistrationToken($obfuscated_token) {
  145. $token = self::deobfuscateToken($obfuscated_token);
  146. $user = self::first(array(
  147. 'conditions' => array('registration_token' => $token)
  148. ));
  149. if (!$user || $token != $user->registration_token)
  150. return false;
  151. $user->confirmRegistration();
  152. return true;
  153. }
  154. function confirmRegistration() {
  155. $this->clearRegistrationToken();
  156. $this->composeWelcomeMail();
  157. }
  158. function clearRegistrationToken() {
  159. $this->update_attribute('registration_token', null);
  160. }
  161. function composeWelcomeMail() {
  162. $this->sendWelcomeMail($this->username, $this->generatePassword());
  163. }
  164. function generatePassword() {
  165. $class_name = get_class($this);
  166. $password = Security::generateRandomString($class_name::PASSWORD_LENGTH);
  167. $this->update_attribute('password_hash', Security::hash($password));
  168. return $password;
  169. }
  170. function composeConfirmationMail() {
  171. $this->sendConfirmationMail($this->username, $this->obfuscateToken());
  172. }
  173. function obfuscateToken() {
  174. $obfuscated = '';
  175. foreach (range(0, strlen($this->registration_token) - 1) as $i)
  176. $obfuscated .= $this->registration_token[$i] . chr(rand(97, 122));
  177. return $obfuscated;
  178. }
  179. static function deobfuscateToken($obfuscated) {
  180. $token = '';
  181. foreach (range(0, strlen($obfuscated) - 1, 2) as $i)
  182. $token .= $obfuscated[$i];
  183. return $token;
  184. }
  185. function isConfirmed() {
  186. return $this->registration_token === null;
  187. }
  188. abstract function sendConfirmationMail($username, $token);
  189. abstract function sendWelcomeMail($username, $password);
  190. }
  191. ?>