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

/application/classes/model/user.php

https://github.com/paulcinelli/kohanajobs
PHP | 358 lines | 200 code | 63 blank | 95 comment | 19 complexity | 96b122a8d7b0acae2c17d65a33ebc93f MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. class Model_User extends Model_Auth_User {
  3. public function __construct()
  4. {
  5. // Override default min_length
  6. $this->_rules['username']['min_length'] = array(3);
  7. parent::__construct();
  8. }
  9. /**
  10. * Validates login information and logs a user in.
  11. *
  12. * @param array values to check
  13. * @param boolean enable autologin
  14. * @return boolean
  15. */
  16. public function login(array & $array, $remember = FALSE)
  17. {
  18. $array = Validate::factory($array)
  19. ->filter(TRUE, 'trim')
  20. ->rules('username', $this->_rules['username'])
  21. ->rules('password', $this->_rules['password']);
  22. if ($array->check())
  23. {
  24. // Attempt to load the user
  25. $this->where('username', '=', $array['username'])->find();
  26. if ($this->loaded() AND Auth::instance()->login($this, $array['password'], $remember))
  27. {
  28. // Login is successful
  29. return TRUE;
  30. }
  31. else
  32. {
  33. $array->error('username', 'invalid');
  34. }
  35. }
  36. return FALSE;
  37. }
  38. /**
  39. * Validates signup information and creates a new user.
  40. *
  41. * @param array values to check
  42. * @return boolean
  43. */
  44. public function signup(array & $array)
  45. {
  46. // Validation setup
  47. $array = Validate::factory($array)
  48. ->filter(TRUE, 'trim')
  49. ->rules('username', $this->_rules['username'])
  50. ->rules('email', $this->_rules['email'])
  51. ->rules('password', $this->_rules['password'])
  52. ->rules('password_confirm', $this->_rules['password_confirm'])
  53. ->callback('username', array($this, 'username_available'))
  54. ->callback('email', array($this, 'email_available'));
  55. if ($status = $array->check())
  56. {
  57. // Add user
  58. $status = $this->values($array)->save();
  59. // Give user the "login" role
  60. $this->add('roles', ORM::factory('role', array('name' => 'login')));
  61. // Create e-mail body with account confirmation link
  62. $body = View::factory('email/confirm_signup', $this->as_array())
  63. ->set('code', Auth::instance()->hash_password($this->email));
  64. // Get the email configuration
  65. $config = Kohana::config('email');
  66. // Load Swift Mailer
  67. require_once Kohana::find_file('vendor', 'swiftmailer/lib/swift_required');
  68. // Create an email message
  69. $message = Swift_Message::newInstance()
  70. ->setSubject('KohanaJobs Sign-up')
  71. ->setFrom(array('info@kohanajobs.com' => 'KohanaJobs.com'))
  72. ->setTo(array($this->email => $this->username))
  73. ->setBody($body);
  74. // Connect to the server
  75. $transport = Swift_SmtpTransport::newInstance($config->server)
  76. ->setUsername($config->username)
  77. ->setPassword($config->password);
  78. // Send the message
  79. Swift_Mailer::newInstance($transport)->send($message);
  80. }
  81. return $status;
  82. }
  83. /**
  84. * Confirms a user signup by validating the confirmation link.
  85. *
  86. * @param integer user id
  87. * @param string confirmation code
  88. * @return boolean
  89. */
  90. public function confirm_signup($user_id, $code)
  91. {
  92. $this->find($user_id);
  93. if ( ! $this->loaded())
  94. {
  95. // Invalid user id
  96. return FALSE;
  97. }
  98. if ($this->has('roles', ORM::factory('role', array('name' => 'user'))))
  99. {
  100. // User is already confirmed
  101. return FALSE;
  102. }
  103. if ($code !== Auth::instance()->hash_password($this->email, Auth::instance()->find_salt($code)))
  104. {
  105. // Invalid confirmation code
  106. return FALSE;
  107. }
  108. // Give the user the "user" role
  109. $this->add('roles', ORM::factory('role', array('name' => 'user')));
  110. return TRUE;
  111. }
  112. public function reset_password(array & $array)
  113. {
  114. // Validation setup
  115. $array = Validate::factory($array)
  116. ->filter(TRUE, 'trim')
  117. ->rules('email', $this->_rules['email'])
  118. ->callback('email', array($this, 'email_not_available'));
  119. if ( ! $array->check())
  120. return FALSE;
  121. // Load user data
  122. $this->where('email', '=', $array['email'])->find();
  123. // Create e-mail body with reset password link
  124. $time = time();
  125. $body = View::factory('email/confirm_reset_password', $this->as_array())
  126. // @todo set('link', 'full url')
  127. ->set('code', Auth::instance()->hash_password($this->email.'+'.$this->password.'+'.$this->last_login.'+'.$time))
  128. ->set('time', $time);
  129. // Get the email configuration
  130. $config = Kohana::config('email');
  131. // Load Swift Mailer
  132. require_once Kohana::find_file('vendor', 'swiftmailer/lib/swift_required');
  133. // Create an email message
  134. $message = Swift_Message::newInstance()
  135. ->setSubject('KohanaJobs - Reset password')
  136. ->setFrom(array('info@kohanajobs.com' => 'KohanaJobs.com'))
  137. ->setTo(array($this->email => $this->username))
  138. ->setBody($body);
  139. // Connect to the server
  140. $transport = Swift_SmtpTransport::newInstance($config->server)
  141. ->setUsername($config->username)
  142. ->setPassword($config->password);
  143. // Send the message
  144. Swift_Mailer::newInstance($transport)->send($message);
  145. return TRUE;
  146. }
  147. /**
  148. * Validates an array for a matching password and password_confirm field.
  149. *
  150. * @param array values to check
  151. * @return boolean
  152. */
  153. public function change_password(array & $array, $_useless_redirect = 'param required for compatibility')
  154. {
  155. $array = Validate::factory($array)
  156. ->filter(TRUE, 'trim')
  157. ->rules('old_password', $this->_rules['password'])
  158. ->rules('password', $this->_rules['password'])
  159. ->rules('password_confirm', $this->_rules['password_confirm'])
  160. ->callback('old_password', array($this, 'check_password'));
  161. if ($status = $array->check())
  162. {
  163. // Change the password
  164. $this->password = $array['password'];
  165. $status = $this->save();
  166. }
  167. return $status;
  168. }
  169. /**
  170. * Step 1 in an email address change.
  171. *
  172. * @param array values to check
  173. * @return boolean
  174. */
  175. public function change_email(array & $array)
  176. {
  177. $array = Validate::factory($array)
  178. ->filter(TRUE, 'trim')
  179. ->rules('password', $this->_rules['password'])
  180. ->rules('email', $this->_rules['email'])
  181. ->callback('password', array($this, 'check_password'))
  182. ->callback('email', array($this, 'email_available'));
  183. // We need to call the check() method first because it resets the internal _errors property.
  184. $array->check();
  185. // Now do a manual check to see whether the new and current email aren't the same
  186. if ($array['email'] == $this->email)
  187. {
  188. $array->error('email', 'not_changed');
  189. }
  190. if ($array->errors())
  191. return FALSE;
  192. // Create e-mail body with email change confirmation link
  193. $body = View::factory('email/confirm_email', $this->as_array())
  194. // @todo set('link', 'full url')
  195. ->set('code', Auth::instance()->hash_password($this->email.'+'.$array['email']))
  196. ->set('new_email', $array['email']);
  197. // Get the email configuration
  198. $config = Kohana::config('email');
  199. // Load Swift Mailer
  200. require_once Kohana::find_file('vendor', 'swiftmailer/lib/swift_required');
  201. // Create an email message
  202. $message = Swift_Message::newInstance()
  203. ->setSubject('KohanaJobs - Change email')
  204. ->setFrom(array('info@kohanajobs.com' => 'KohanaJobs.com'))
  205. ->setTo(array($array['email'] => $this->username))
  206. ->setBody($body);
  207. // Connect to the server
  208. $transport = Swift_SmtpTransport::newInstance($config->server)
  209. ->setUsername($config->username)
  210. ->setPassword($config->password);
  211. // Send the message
  212. Swift_Mailer::newInstance($transport)->send($message);
  213. return TRUE;
  214. }
  215. public function confirm_email($user_id, $code, $new_email)
  216. {
  217. // Email was base64 encoded in URL in order to make it less visible that the URL contains an email address,
  218. // not that that would be an immediate security threat. Base64 encoding makes urlencoding not necessary anymore.
  219. // Urlencoding would leave dots as is, which would require a custom route regex for that segment.
  220. $new_email = base64_decode($email);
  221. if ( ! Validate::email($new_email))
  222. {
  223. // Invalid email
  224. return FALSE;
  225. }
  226. $this->find($user_id);
  227. if ( ! $this->loaded())
  228. {
  229. // Invalid user id
  230. return FALSE;
  231. }
  232. if ($code !== Auth::instance()->hash_password($this->email.'+'.$new_email, Auth::instance()->find_salt($code)))
  233. {
  234. // Invalid confirmation code
  235. return FALSE;
  236. }
  237. if ($this->unique_key_exists($new_email, 'email'))
  238. {
  239. // New email is already taken
  240. return FALSE;
  241. }
  242. // Actually change the email for the user in db
  243. $this->email = $new_email;
  244. $this->save();
  245. // It could be that the user changes his email address before he confirmed his signup.
  246. // In that case, the original confirm_signup link gets invalid automatically because it uses the email as hashed confirmation code.
  247. // No problem, though, if the user confirms his new email address, we can as well confirm his account right here.
  248. if ( ! $this->has('roles', ORM::factory('role', array('name' => 'user'))))
  249. {
  250. // Give the user the "user" role
  251. $this->add('roles', ORM::factory('role', array('name' => 'user')));
  252. }
  253. return TRUE;
  254. }
  255. /**
  256. * Validates the password for the currently logged in user.
  257. * Validation callback.
  258. *
  259. * @param object Validate
  260. * @param string field name
  261. * @return void
  262. */
  263. public function check_password(Validate $array, $field)
  264. {
  265. if ($user = Auth::instance()->get_user())
  266. {
  267. $stored_password = Auth::instance()->password($user->username);
  268. $salt = Auth::instance()->find_salt($stored_password);
  269. if ($stored_password === Auth::instance()->hash_password($array[$field], $salt))
  270. {
  271. // Correct password
  272. return;
  273. }
  274. }
  275. $array->error($field, 'check_password');
  276. }
  277. public function email_not_available(Validate $array, $field)
  278. {
  279. if ( ! $this->unique_key_exists($array[$field], 'email'))
  280. {
  281. $array->error($field, 'email_not_available', array($array[$field]));
  282. }
  283. }
  284. /**
  285. * Allows a model use both email and username as unique identifiers.
  286. * This method also adds support for the id field.
  287. *
  288. * @param mixed unique value
  289. * @return string field name
  290. */
  291. public function unique_key($value)
  292. {
  293. return (is_int($value)) ? 'id' : parent::unique_key($value);
  294. }
  295. }