Chat token class clean-up.
This commit is contained in:
parent
c17897b505
commit
efee8cb67b
4 changed files with 110 additions and 95 deletions
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
namespace Misuzu\Http\Handlers;
|
||||
|
||||
use Exception;
|
||||
use HttpResponse;
|
||||
use HttpRequest;
|
||||
use Misuzu\Base64;
|
||||
|
@ -9,8 +8,11 @@ use Misuzu\Config;
|
|||
use Misuzu\DB;
|
||||
use Misuzu\Emoticon;
|
||||
use Misuzu\Stream;
|
||||
use Misuzu\Users\ChatToken;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserChatToken;
|
||||
use Misuzu\Users\UserChatTokenNotFoundException;
|
||||
use Misuzu\Users\UserChatTokenCreationFailedException;
|
||||
|
||||
final class SockChatHandler extends Handler {
|
||||
private string $hashKey = 'woomy';
|
||||
|
@ -127,7 +129,9 @@ final class SockChatHandler extends Handler {
|
|||
}
|
||||
|
||||
public function login(HttpResponse $response, HttpRequest $request) {
|
||||
if(!user_session_active()) {
|
||||
$currentUser = User::getCurrent();
|
||||
|
||||
if($currentUser === null) {
|
||||
$response->redirect(url('auth-login'));
|
||||
return;
|
||||
}
|
||||
|
@ -135,9 +139,8 @@ final class SockChatHandler extends Handler {
|
|||
$params = $request->getQueryParams();
|
||||
|
||||
try {
|
||||
$token = ChatToken::create(user_session_current('user_id'));
|
||||
} catch(Exception $ex) {
|
||||
$response->setHeader('X-SharpChat-Error', $ex->getMessage());
|
||||
$token = UserChatToken::create($currentUser);
|
||||
} catch(UserChatTokenNotFoundException $ex) {
|
||||
return 500;
|
||||
}
|
||||
|
||||
|
@ -203,10 +206,16 @@ final class SockChatHandler extends Handler {
|
|||
if(!hash_equals($realHash, $userHash))
|
||||
return ['success' => false, 'reason' => 'hash'];
|
||||
|
||||
try {
|
||||
$userInfo = User::byId($authInfo->user_id);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return ['success' => false, 'reason' => 'user'];
|
||||
}
|
||||
|
||||
$authMethod = mb_substr($authInfo->token, 0, 5);
|
||||
|
||||
if($authMethod === 'PASS:') {
|
||||
if(time() > 1577750400)
|
||||
//if(time() > 1577750400)
|
||||
return ['success' => false, 'reason' => 'unsupported'];
|
||||
|
||||
//if(user_password_verify_db($authInfo->user_id, mb_substr($authInfo->token, 5)))
|
||||
|
@ -227,11 +236,11 @@ final class SockChatHandler extends Handler {
|
|||
$userId = user_session_current('user_id');
|
||||
user_bump_last_active($userId);
|
||||
user_session_bump_active(user_session_current('session_id'));
|
||||
}
|
||||
} else return ['success' => false, 'reason' => 'expired'];
|
||||
} else {
|
||||
try {
|
||||
$token = ChatToken::get($authInfo->user_id, $authInfo->token);
|
||||
} catch(Exception $ex) {
|
||||
$token = UserChatToken::byExact($userInfo, $authInfo->token);
|
||||
} catch(UserChatTokenCreationFailedException $ex) {
|
||||
return ['success' => false, 'reason' => 'token'];
|
||||
}
|
||||
|
||||
|
@ -239,18 +248,8 @@ final class SockChatHandler extends Handler {
|
|||
$token->delete();
|
||||
return ['success' => false, 'reason' => 'expired'];
|
||||
}
|
||||
|
||||
$userId = $token->getUserId();
|
||||
}
|
||||
|
||||
if(!isset($userId) || $userId < 1)
|
||||
return ['success' => false, 'reason' => 'unknown'];
|
||||
|
||||
$userInfo = User::byId($userId);
|
||||
|
||||
if($userInfo === null)
|
||||
return ['success' => false, 'reason' => 'user'];
|
||||
|
||||
$perms = self::PERMS_DEFAULT;
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_MANAGE_USERS))
|
||||
|
|
|
@ -41,8 +41,6 @@ class NewsPost implements JsonSerializable {
|
|||
. ', UNIX_TIMESTAMP(%1$s.`post_updated`) AS `post_updated`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`post_deleted`) AS `post_deleted`';
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function getId(): int {
|
||||
return $this->post_id < 1 ? -1 : $this->post_id;
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use Misuzu\DB;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
final class ChatToken {
|
||||
public const TOKEN_LIFETIME = 60 * 60 * 24 * 7;
|
||||
|
||||
public function getUserId(): int {
|
||||
return $this->user_id ?? 0;
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
return $this->token_string ?? '';
|
||||
}
|
||||
|
||||
public function getCreationTime(): int {
|
||||
return $this->token_created ?? 0;
|
||||
}
|
||||
public function getExpirationTime(): int {
|
||||
return $this->getCreationTime() + self::TOKEN_LIFETIME;
|
||||
}
|
||||
public function hasExpired(): bool {
|
||||
return $this->getExpirationTime() <= time();
|
||||
}
|
||||
|
||||
public function delete(): void {
|
||||
DB::prepare('
|
||||
DELETE FROM `msz_user_chat_tokens`
|
||||
WHERE `user_id` = :user,
|
||||
AND `token_string` = :token
|
||||
')->bind('user', $this->getUserId())
|
||||
->bind('token', $this->getToken())
|
||||
->execute();
|
||||
}
|
||||
|
||||
public static function create(int $userId): self {
|
||||
if($userId < 1)
|
||||
throw new InvalidArgumentException('Invalid user id.');
|
||||
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$create = DB::prepare('
|
||||
INSERT INTO `msz_user_chat_tokens` (`user_id`, `token_string`)
|
||||
VALUES (:user, :token)
|
||||
')->bind('user', $userId)->bind('token', $token)->execute();
|
||||
|
||||
if(!$create)
|
||||
throw new RuntimeException('Token creation failed.');
|
||||
|
||||
return self::get($userId, $token);
|
||||
}
|
||||
|
||||
public static function get(int $userId, string $token): self {
|
||||
if($userId < 1)
|
||||
throw new InvalidArgumentException('Invalid user id.');
|
||||
if(strlen($token) !== 64)
|
||||
throw new InvalidArgumentException('Invalid token string.');
|
||||
|
||||
$token = DB::prepare('
|
||||
SELECT `user_id`, `token_string`, UNIX_TIMESTAMP(`token_created`) AS `token_created`
|
||||
FROM `msz_user_chat_tokens`
|
||||
WHERE `user_id` = :user
|
||||
AND `token_string` = :token
|
||||
')->bind('user', $userId)->bind('token', $token)->fetchObject(self::class);
|
||||
|
||||
if(empty($token))
|
||||
throw new RuntimeException('Token not found.');
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
91
src/Users/UserChatToken.php
Normal file
91
src/Users/UserChatToken.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use Misuzu\DB;
|
||||
|
||||
class UserChatTokenException extends UsersException {}
|
||||
class UserChatTokenNotFoundException extends UserChatTokenException {}
|
||||
class UserChatTokenCreationFailedException extends UserChatTokenException {}
|
||||
|
||||
class UserChatToken {
|
||||
// Database fields
|
||||
private $user_id = -1;
|
||||
private $token_string = '';
|
||||
private $token_created = null;
|
||||
|
||||
private $user = null;
|
||||
|
||||
public const TOKEN_LIFETIME = 60 * 60 * 24 * 7;
|
||||
|
||||
public const TABLE = 'user_chat_tokens';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`user_id`, %1$s.`token_string`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`token_created`) AS `token_created`';
|
||||
|
||||
public function getUserId(): int {
|
||||
return $this->user_id < 1 ? -1 : $this->user_id;
|
||||
}
|
||||
public function getUser(): User {
|
||||
if($this->user === null)
|
||||
$this->user = User::byId($this->getUserId());
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
return $this->token_string;
|
||||
}
|
||||
|
||||
public function getCreatedTime(): int {
|
||||
return $this->token_created === null ? -1 : $this->token_created;
|
||||
}
|
||||
public function getExpirationTime(): int {
|
||||
return $this->getCreatedTime() + self::TOKEN_LIFETIME;
|
||||
}
|
||||
public function hasExpired(): bool {
|
||||
return $this->getExpirationTime() <= time();
|
||||
}
|
||||
|
||||
public function delete(): void {
|
||||
DB::prepare('
|
||||
DELETE FROM `msz_user_chat_tokens`
|
||||
WHERE `user_id` = :user,
|
||||
AND `token_string` = :token
|
||||
')->bind('user', $this->getUserId())
|
||||
->bind('token', $this->getToken())
|
||||
->execute();
|
||||
}
|
||||
|
||||
public static function generateToken(): string {
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
public static function create(User $user): self {
|
||||
$token = self::generateToken();
|
||||
$create = DB::prepare('
|
||||
INSERT INTO `msz_user_chat_tokens` (`user_id`, `token_string`)
|
||||
VALUES (:user, :token)
|
||||
') ->bind('user', $user->getId())
|
||||
->bind('token', $token)
|
||||
->execute();
|
||||
|
||||
if(!$create)
|
||||
throw new UserChatTokenCreationFailedException;
|
||||
|
||||
return self::byExact($user, $token);
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byExact(User $user, string $token): self {
|
||||
$tokenInfo = DB::prepare(self::byQueryBase() . ' WHERE `user_id` = :user AND `token_string` = :token')
|
||||
->bind('user', $user->getId())
|
||||
->bind('token', $token)
|
||||
->fetchObject(self::class);
|
||||
|
||||
if(!$tokenInfo)
|
||||
throw new UserChatTokenNotFoundException;
|
||||
|
||||
return $tokenInfo;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue