2021-06-26 11:23:15 -04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace BookStack\Auth\Access;
|
2019-08-17 10:52:33 -04:00
|
|
|
|
|
|
|
use BookStack\Auth\User;
|
|
|
|
use BookStack\Exceptions\UserTokenExpiredException;
|
|
|
|
use BookStack\Exceptions\UserTokenNotFoundException;
|
|
|
|
use Carbon\Carbon;
|
2021-08-31 16:50:23 -04:00
|
|
|
use Illuminate\Support\Facades\DB;
|
2019-09-13 18:58:40 -04:00
|
|
|
use Illuminate\Support\Str;
|
2019-08-17 10:52:33 -04:00
|
|
|
use stdClass;
|
|
|
|
|
|
|
|
class UserTokenService
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Name of table where user tokens are stored.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $tokenTable = 'user_tokens';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Token expiry time in hours.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $expiryTime = 24;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete all email confirmations that belong to a user.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param User $user
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function deleteByUser(User $user)
|
|
|
|
{
|
2021-08-31 16:50:23 -04:00
|
|
|
return DB::table($this->tokenTable)
|
2019-08-17 10:52:33 -04:00
|
|
|
->where('user_id', '=', $user->id)
|
|
|
|
->delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the user id from a token, while check the token exists and has not expired.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param string $token
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @throws UserTokenNotFoundException
|
|
|
|
* @throws UserTokenExpiredException
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
|
|
|
* @return int
|
2019-08-17 10:52:33 -04:00
|
|
|
*/
|
2021-06-26 11:23:15 -04:00
|
|
|
public function checkTokenAndGetUserId(string $token): int
|
2019-08-17 10:52:33 -04:00
|
|
|
{
|
|
|
|
$entry = $this->getEntryByToken($token);
|
|
|
|
|
|
|
|
if (is_null($entry)) {
|
|
|
|
throw new UserTokenNotFoundException('Token "' . $token . '" not found');
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->entryExpired($entry)) {
|
2019-08-18 08:55:28 -04:00
|
|
|
throw new UserTokenExpiredException("Token of id {$entry->id} has expired.", $entry->user_id);
|
2019-08-17 10:52:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return $entry->user_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a unique token within the email confirmation database.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return string
|
|
|
|
*/
|
2021-06-26 11:23:15 -04:00
|
|
|
protected function generateToken(): string
|
2019-08-17 10:52:33 -04:00
|
|
|
{
|
2019-09-13 18:58:40 -04:00
|
|
|
$token = Str::random(24);
|
2019-08-17 10:52:33 -04:00
|
|
|
while ($this->tokenExists($token)) {
|
2019-09-13 18:58:40 -04:00
|
|
|
$token = Str::random(25);
|
2019-08-17 10:52:33 -04:00
|
|
|
}
|
2021-06-26 11:23:15 -04:00
|
|
|
|
2019-08-17 10:52:33 -04:00
|
|
|
return $token;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate and store a token for the given user.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param User $user
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return string
|
|
|
|
*/
|
2021-06-26 11:23:15 -04:00
|
|
|
protected function createTokenForUser(User $user): string
|
2019-08-17 10:52:33 -04:00
|
|
|
{
|
|
|
|
$token = $this->generateToken();
|
2021-08-31 16:50:23 -04:00
|
|
|
DB::table($this->tokenTable)->insert([
|
2021-06-26 11:23:15 -04:00
|
|
|
'user_id' => $user->id,
|
|
|
|
'token' => $token,
|
2019-08-17 10:52:33 -04:00
|
|
|
'created_at' => Carbon::now(),
|
2021-06-26 11:23:15 -04:00
|
|
|
'updated_at' => Carbon::now(),
|
2019-08-17 10:52:33 -04:00
|
|
|
]);
|
2021-06-26 11:23:15 -04:00
|
|
|
|
2019-08-17 10:52:33 -04:00
|
|
|
return $token;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given token exists.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param string $token
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return bool
|
|
|
|
*/
|
2021-06-26 11:23:15 -04:00
|
|
|
protected function tokenExists(string $token): bool
|
2019-08-17 10:52:33 -04:00
|
|
|
{
|
2021-08-31 16:50:23 -04:00
|
|
|
return DB::table($this->tokenTable)
|
2019-08-17 10:52:33 -04:00
|
|
|
->where('token', '=', $token)->exists();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a token entry for the given token.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param string $token
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return object|null
|
|
|
|
*/
|
|
|
|
protected function getEntryByToken(string $token)
|
|
|
|
{
|
2021-08-31 16:50:23 -04:00
|
|
|
return DB::table($this->tokenTable)
|
2019-08-17 10:52:33 -04:00
|
|
|
->where('token', '=', $token)
|
|
|
|
->first();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given token entry has expired.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @param stdClass $tokenEntry
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2019-08-17 10:52:33 -04:00
|
|
|
* @return bool
|
|
|
|
*/
|
2021-06-26 11:23:15 -04:00
|
|
|
protected function entryExpired(stdClass $tokenEntry): bool
|
2019-08-17 10:52:33 -04:00
|
|
|
{
|
|
|
|
return Carbon::now()->subHours($this->expiryTime)
|
|
|
|
->gt(new Carbon($tokenEntry->created_at));
|
|
|
|
}
|
2019-09-15 13:29:51 -04:00
|
|
|
}
|