Chat token class clean-up.

This commit is contained in:
flash 2020-05-22 13:27:23 +00:00
parent c17897b505
commit efee8cb67b
4 changed files with 110 additions and 95 deletions

View file

@ -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))

View file

@ -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;
}

View file

@ -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;
}
}

View 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;
}
}