From efee8cb67b28c2ad609e58b90b4d3e87427a1221 Mon Sep 17 00:00:00 2001 From: flashwave Date: Fri, 22 May 2020 13:27:23 +0000 Subject: [PATCH] Chat token class clean-up. --- src/Http/Handlers/SockChatHandler.php | 39 ++++++------ src/News/NewsPost.php | 2 - src/Users/ChatToken.php | 73 --------------------- src/Users/UserChatToken.php | 91 +++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 95 deletions(-) delete mode 100644 src/Users/ChatToken.php create mode 100644 src/Users/UserChatToken.php diff --git a/src/Http/Handlers/SockChatHandler.php b/src/Http/Handlers/SockChatHandler.php index 437ed714..693a3fdd 100644 --- a/src/Http/Handlers/SockChatHandler.php +++ b/src/Http/Handlers/SockChatHandler.php @@ -1,7 +1,6 @@ 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)) diff --git a/src/News/NewsPost.php b/src/News/NewsPost.php index aaee680b..3423cdaa 100644 --- a/src/News/NewsPost.php +++ b/src/News/NewsPost.php @@ -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; } diff --git a/src/Users/ChatToken.php b/src/Users/ChatToken.php deleted file mode 100644 index 40a0fb6b..00000000 --- a/src/Users/ChatToken.php +++ /dev/null @@ -1,73 +0,0 @@ -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; - } -} diff --git a/src/Users/UserChatToken.php b/src/Users/UserChatToken.php new file mode 100644 index 00000000..1c32b87d --- /dev/null +++ b/src/Users/UserChatToken.php @@ -0,0 +1,91 @@ +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; + } +}