/src/Psecio/Gatekeeper/Session/RememberMe.php

https://github.com/psecio/gatekeeper · PHP · 266 lines · 133 code · 31 blank · 102 comment · 17 complexity · c9d7722a53dfb447df415736d1991a2c MD5 · raw file

  1. <?php
  2. namespace Psecio\Gatekeeper\Session;
  3. class RememberMe
  4. {
  5. /**
  6. * Token name
  7. * @var string
  8. */
  9. private $tokenName = 'gktoken';
  10. /**
  11. * Default expiration time
  12. * @var string
  13. */
  14. private $expireInterval = '+14 days';
  15. /**
  16. * Data (cookie) for use in token evaluation
  17. * @var array
  18. */
  19. private $data = array();
  20. /**
  21. * User instance to check against
  22. * @var \Psecio\Gatekeeper\UserModel
  23. */
  24. private $user;
  25. /**
  26. * Datasource for use in making find//save requests
  27. * @var \Psecio\Gatekeeper\DataSource
  28. */
  29. private $datasource;
  30. /**
  31. * Init the object and set up the datasource, data and possibly a user
  32. *
  33. * @param \Psecio\Gatekeeper\DataSource $datasource Data source to use for operations
  34. * @param array $data Data to use in evaluation
  35. * @param \Psecio\Gatekeeper\UserModel|null $user User model instance [optional]
  36. */
  37. public function __construct(\Psecio\Gatekeeper\DataSource $datasource, array $data, \Psecio\Gatekeeper\UserModel $user = null)
  38. {
  39. $this->datasource = $datasource;
  40. if (!empty($data)) {
  41. $this->data = $data;
  42. }
  43. if ($user !== null) {
  44. $this->user = $user;
  45. }
  46. if (isset($this->data['interval'])) {
  47. $this->expireInterval = $this->data['interval'];
  48. }
  49. }
  50. /**
  51. * Get the current data for the evaluation
  52. */
  53. public function getData()
  54. {
  55. return $this->data;
  56. }
  57. /**
  58. * Get the current user for evaluation
  59. */
  60. public function getUser()
  61. {
  62. return $this->user;
  63. }
  64. /**
  65. * Get the current expiration interval
  66. */
  67. public function getExpireInterval()
  68. {
  69. return $this->expireInterval;
  70. }
  71. /**
  72. * Setup the "remember me" session and cookies
  73. *
  74. * @param \Psecio\Gatekeeper\UserModel|null $user User model instance [optional]
  75. * @return boolean Success/fail of setting up the session/cookies
  76. */
  77. public function setup(\Psecio\Gatekeeper\UserModel $user = null)
  78. {
  79. $user = ($user === null) ? $this->user : $user;
  80. $userToken = $this->getUserToken($user);
  81. if ($userToken->id !== null || $this->isExpired($userToken)) {
  82. return false;
  83. }
  84. $token = $this->generateToken();
  85. $tokenModel = $this->saveToken($token, $user);
  86. if ($tokenModel === false) {
  87. return false;
  88. }
  89. $this->setCookies($tokenModel, $token);
  90. return true;
  91. }
  92. /**
  93. * Verify the token if it exists
  94. * Removes the old token and sets up a new one if valid
  95. *
  96. * @param \Psecio\Gatekeeper\AuthTokenModel $token Token model instance
  97. * @return boolean Pass/fail result of the validation
  98. */
  99. public function verify(\Psecio\Gatekeeper\AuthTokenModel $token = null)
  100. {
  101. if (!isset($this->data[$this->tokenName])) {
  102. return false;
  103. }
  104. if ($token === null) {
  105. $tokenParts = explode(':', $this->data[$this->tokenName]);
  106. $token = $this->getById($tokenParts[0]);
  107. }
  108. if ($token === false) {
  109. return false;
  110. }
  111. $user = $token->user;
  112. $userToken = $token->token;
  113. // Remove the token (a new one will be made later)
  114. $this->datasource->delete($token);
  115. if (\Psecio\Gatekeeper\Gatekeeper::hash_equals($this->data[$this->tokenName], $token->id.':'.hash('sha256', $userToken)) === false) {
  116. return false;
  117. }
  118. $this->setup($user);
  119. return $user;
  120. }
  121. /**
  122. * Get the token information searching on given token string
  123. *
  124. * @param string $tokenValue Token string for search
  125. * @return boolean|\Psecio\Gatekeeper\AuthTokenModel Instance if no query errors
  126. */
  127. public function getByToken($tokenValue)
  128. {
  129. $token = new \Psecio\Gatekeeper\AuthTokenModel($this->datasource);
  130. $result = $this->datasource->find($token, array('token' => $tokenValue));
  131. return $result;
  132. }
  133. /**
  134. * Get a token by its unique ID
  135. *
  136. * @param integer $tokenId Token ID
  137. * @return boolean|\Psecio\Gatekeeper\AuthTokenModel instance
  138. */
  139. public function getById($tokenId)
  140. {
  141. $token = new \Psecio\Gatekeeper\AuthTokenModel($this->datasource);
  142. $result = $this->datasource->find($token, array('id' => $tokenId));
  143. return $result;
  144. }
  145. /**
  146. * Get the token by user ID
  147. * Also performs evaluation to check if token is expired, returns false if so
  148. *
  149. * @param \Psecio\Gatekeeper\UserModel $user User model instance
  150. * @return boolean|\Psecio\Gatekeeper\AuthTokenModel instance
  151. */
  152. public function getUserToken(\Psecio\Gatekeeper\UserModel $user)
  153. {
  154. $tokenModel = new \Psecio\Gatekeeper\AuthTokenModel($this->datasource);
  155. return $this->datasource->find($tokenModel, array('userId' => $user->id));
  156. }
  157. /**
  158. * Check to see if the token has expired
  159. *
  160. * @param \Psecio\Gatekeeper\AuthTokenModel $token Token model instance
  161. * @param boolean $delete Delete/don't delete the token if expired [optional]
  162. * @return boolean Token expired/not expired
  163. */
  164. public function isExpired(\Psecio\Gatekeeper\AuthTokenModel $token, $delete = true)
  165. {
  166. if ($token->expires !== null && new \Datetime() > new \DateTime($token->expires)) {
  167. if ($delete === true) {
  168. $this->deleteToken($token->token);
  169. }
  170. return true;
  171. }
  172. return false;
  173. }
  174. /**
  175. * Save the new token to the data source
  176. *
  177. * @param string $token Token string
  178. * @param \Psecio\Gatekeeper\UserModel $user User model instance
  179. * @return boolean|\Psecio\Gatekeeper\AuthTokenModel Success/fail of token creation or AuthTokenModel instance
  180. */
  181. public function saveToken($token, \Psecio\Gatekeeper\UserModel $user)
  182. {
  183. $expires = new \DateTime($this->expireInterval);
  184. $tokenModel = new \Psecio\Gatekeeper\AuthTokenModel($this->datasource, array(
  185. 'token' => $token,
  186. 'userId' => $user->id,
  187. 'expires' => $expires->format('Y-m-d H:i:s')
  188. ));
  189. $result = $this->datasource->save($tokenModel);
  190. return ($result === false) ? false : $tokenModel;
  191. }
  192. /**
  193. * Delete the token by token string
  194. *
  195. * @param string $token Token hash string
  196. * @return boolean Success/fail of token record deletion
  197. */
  198. public function deleteToken($token)
  199. {
  200. $tokenModel = new \Psecio\Gatekeeper\AuthTokenModel($this->datasource);
  201. $token = $this->datasource->find($tokenModel, array('token' => $token));
  202. if ($token !== false) {
  203. return $this->datasource->delete($token);
  204. }
  205. return false;
  206. }
  207. /**
  208. * Generate the token value
  209. *
  210. * @return string Token hash
  211. */
  212. public function generateToken()
  213. {
  214. $factory = new \RandomLib\Factory;
  215. $generator = $factory->getMediumStrengthGenerator();
  216. return base64_encode($generator->generate(24));
  217. }
  218. /**
  219. * Set the cookies with the main and auth tokens
  220. *
  221. * @param \Psecio\Gatekeeper\AuthTokenModel $tokenModel Auth token model instance
  222. * @param string $token Token hash
  223. * @param boolean $https Enable/disable HTTPS setting on cookies [optional]
  224. * @param string $domain Domain value to set cookies on
  225. */
  226. public function setCookies(\Psecio\Gatekeeper\AuthTokenModel $tokenModel, $token, $https = false, $domain = null)
  227. {
  228. if ($domain === null && isset($_SERVER['HTTP_HOST'])) {
  229. $domain = ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false;
  230. }
  231. $tokenValue = $tokenModel->id.':'.hash('sha256', $token);
  232. $expires = new \DateTime($this->expireInterval);
  233. return setcookie($this->tokenName, $tokenValue, $expires->format('U'), '/', $domain, $https, true);
  234. }
  235. }