classMapper = $classMapper; $this->algorithm = $algorithm; } /** * Cancels a specified token by removing it from the database. * * @param int $token The token to remove. * @return Model|false */ public function cancel($token) { // Hash the password reset token for the stored version $hash = hash($this->algorithm, $token); // Find an incomplete reset request for the specified hash $model = $this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'hash', $hash) ->where('completed', FALSE) ->first(); if ($model === NULL) { return FALSE; } $model->delete(); return $model; } /** * Completes a token-based process, invoking updateUser() in the child object to do the actual action. * * @param int $token The token to complete. * @param mixed[] $userParams An optional list of parameters to pass to updateUser(). * @return Model|false */ public function complete($token, $userParams = []) { // Hash the token for the stored version $hash = hash($this->algorithm, $token); // Find an unexpired, incomplete token for the specified hash $model = $this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'hash', $hash) ->where('completed', FALSE) ->where('expires_at', '>', Carbon::now()) ->first(); if ($model === NULL) { return FALSE; } // Fetch user for this token $user = $this->classMapper->staticMethod('user', 'find', $model->user_id); if (is_null($user)) { return FALSE; } $this->updateUser($user, $userParams); $model->fill([ 'completed' => TRUE, 'completed_at' => Carbon::now() ]); $model->save(); return $model; } /** * Create a new token for a specified user. * * @param User $user The user object to associate with this token. * @param int $timeout The time, in seconds, after which this token should expire. * @return Model The model (PasswordReset, Verification, etc) object that stores the token. */ public function create(User $user, $timeout) { // Remove any previous tokens for this user $this->removeExisting($user); // Compute expiration time $expiresAt = Carbon::now()->addSeconds($timeout); $model = $this->classMapper->createInstance($this->modelIdentifier); // Generate a random token $model->setToken($this->generateRandomToken()); // Hash the password reset token for the stored version $hash = hash($this->algorithm, $model->getToken()); $model->fill([ 'hash' => $hash, 'completed' => FALSE, 'expires_at' => $expiresAt ]); $model->user_id = $user->id; $model->save(); return $model; } /** * Determine if a specified user has an incomplete and unexpired token. * * @param User $user The user object to look up. * @param int $token Optionally, try to match a specific token. * @return Model|false */ public function exists(User $user, $token = NULL) { $model = $this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'user_id', $user->id) ->where('completed', FALSE) ->where('expires_at', '>', Carbon::now()); if ($token) { // get token hash $hash = hash($this->algorithm, $token); $model->where('hash', $hash); } return $model->first() ?: FALSE; } /** * Delete all existing tokens from the database for a particular user. * * @param User $user * @return int */ protected function removeExisting(User $user) { return $this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'user_id', $user->id) ->delete(); } /** * Remove all expired tokens from the database. * * @return bool|null */ public function removeExpired() { return $this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'completed', FALSE) ->where('expires_at', '<', Carbon::now()) ->delete(); } /** * Generate a new random token for this user. * * This generates a token to use for verifying a new account, resetting a lost password, etc. * @param string $gen specify an existing token that, if we happen to generate the same value, we should regenerate on. * @return string */ protected function generateRandomToken($gen = NULL) { do { $gen = md5(uniqid(mt_rand(), FALSE)); } while ($this->classMapper ->staticMethod($this->modelIdentifier, 'where', 'hash', hash($this->algorithm, $gen)) ->first()); return $gen; } /** * Modify the user during the token completion process. * * This method is called during complete(), and is a way for concrete implementations to modify the user. * @param User $user the user object to modify. * @return mixed[] $args the list of parameters that were supplied to the call to `complete()` */ abstract protected function updateUser($user, $args); }