Converted Sharp Chat routes to Index.
This commit is contained in:
parent
6e650923d8
commit
7d6b94294f
5 changed files with 350 additions and 342 deletions
|
@ -105,7 +105,7 @@ if(MSZ_CLI) { // Temporary backwards compatibility measure, remove this later
|
|||
return;
|
||||
}
|
||||
|
||||
$ctx = new MszContext;
|
||||
$ctx = new MszContext(DB::getInstance());
|
||||
|
||||
// Everything below here should eventually be moved to index.php, probably only initialised when required.
|
||||
// Serving things like the css/js doesn't need to initialise sessions.
|
||||
|
|
|
@ -2,18 +2,34 @@
|
|||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Template;
|
||||
use Misuzu\Database\Database;
|
||||
use Misuzu\SharpChat\SharpChatRoutes;
|
||||
use Misuzu\Users\Users;
|
||||
use Index\Http\HttpFx;
|
||||
use Index\Http\HttpRequest;
|
||||
use Index\Routing\Router;
|
||||
|
||||
// this class should function as the root for everything going forward
|
||||
// no more magical static classes that are just kind of assumed to exist
|
||||
// it currently looks Pretty Messy, but most everything else will be holding instances of other classes
|
||||
class MszContext {
|
||||
private Database $database;
|
||||
//private Users $users;
|
||||
private HttpFx $router;
|
||||
|
||||
public function __construct() {
|
||||
public function __construct(Database $database) {
|
||||
$this->database = $database;
|
||||
//$this->users = new Users($this->database);
|
||||
}
|
||||
|
||||
public function getRouter(): Router {
|
||||
return $this->router->getRouter();
|
||||
}
|
||||
|
||||
/*public function getUsers(): Users {
|
||||
return $this->users;
|
||||
}*/
|
||||
|
||||
public function setUpHttp(bool $legacy = false): void {
|
||||
$this->router = new HttpFx;
|
||||
$this->router->use('/', function($response) {
|
||||
|
@ -82,19 +98,7 @@ class MszContext {
|
|||
$this->router->get('/forum/mark-as-read', msz_compat_handler('Forum', 'markAsReadGET'));
|
||||
$this->router->post('/forum/mark-as-read', msz_compat_handler('Forum', 'markAsReadPOST'));
|
||||
|
||||
$this->router->get('/_sockchat/emotes', msz_compat_handler('SockChat', 'emotes'));
|
||||
$this->router->get('/_sockchat/login', msz_compat_handler('SockChat', 'login'));
|
||||
$this->router->get('/_sockchat/resolve', msz_compat_handler('SockChat', 'resolve'));
|
||||
$this->router->post('/_sockchat/bump', msz_compat_handler('SockChat', 'bump'));
|
||||
$this->router->post('/_sockchat/verify', msz_compat_handler('SockChat', 'verify'));
|
||||
$this->router->get('/_sockchat/token', msz_compat_handler('SockChat', 'token'));
|
||||
$this->router->options('/_sockchat/token', msz_compat_handler('SockChat', 'token'));
|
||||
$this->router->get('/_sockchat/profile-check', msz_compat_handler('SockChat', 'profileCheck'));
|
||||
$this->router->options('/_sockchat/profile-check', msz_compat_handler('SockChat', 'profileCheck'));
|
||||
$this->router->get('/_sockchat/bans', msz_compat_handler('SockChat', 'bans'));
|
||||
$this->router->get('/_sockchat/bans/check', msz_compat_handler('SockChat', 'checkBan'));
|
||||
$this->router->post('/_sockchat/bans/create', msz_compat_handler('SockChat', 'createBan'));
|
||||
$this->router->delete('/_sockchat/bans/remove', msz_compat_handler('SockChat', 'removeBan'));
|
||||
new SharpChatRoutes($this);
|
||||
}
|
||||
|
||||
private function registerLegacyRedirects(): void {
|
||||
|
|
48
src/SharpChat/SharpChatPerms.php
Normal file
48
src/SharpChat/SharpChatPerms.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
namespace Misuzu\SharpChat;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
final class SharpChatPerms {
|
||||
private const P_KICK_USER = 0x00000001;
|
||||
private const P_BAN_USER = 0x00000002;
|
||||
private const P_SILENCE_USER = 0x00000004;
|
||||
private const P_BROADCAST = 0x00000008;
|
||||
private const P_SET_OWN_NICK = 0x00000010;
|
||||
private const P_SET_OTHER_NICK = 0x00000020;
|
||||
private const P_CREATE_CHANNEL = 0x00000040;
|
||||
private const P_DELETE_CHANNEL = 0x00010000;
|
||||
private const P_SET_CHAN_PERMA = 0x00000080;
|
||||
private const P_SET_CHAN_PASS = 0x00000100;
|
||||
private const P_SET_CHAN_HIER = 0x00000200;
|
||||
private const P_JOIN_ANY_CHAN = 0x00020000;
|
||||
private const P_SEND_MESSAGE = 0x00000400;
|
||||
private const P_DELETE_OWN_MSG = 0x00000800;
|
||||
private const P_DELETE_ANY_MSG = 0x00001000;
|
||||
private const P_EDIT_OWN_MSG = 0x00002000;
|
||||
private const P_EDIT_ANY_MSG = 0x00004000;
|
||||
private const P_VIEW_IP_ADDR = 0x00008000;
|
||||
|
||||
private const PERMS_DEFAULT = self::P_SEND_MESSAGE | self::P_DELETE_OWN_MSG | self::P_EDIT_OWN_MSG;
|
||||
private const PERMS_MANAGE_USERS = self::P_SET_OWN_NICK | self::P_SET_OTHER_NICK | self::P_DELETE_ANY_MSG
|
||||
| self::P_EDIT_ANY_MSG | self::P_VIEW_IP_ADDR | self::P_BROADCAST;
|
||||
private const PERMS_MANAGE_WARNS = self::P_KICK_USER | self::P_BAN_USER | self::P_SILENCE_USER;
|
||||
private const PERMS_CHANGE_BACKG = self::P_SET_OWN_NICK | self::P_CREATE_CHANNEL | self::P_SET_CHAN_PASS;
|
||||
private const PERMS_MANAGE_FORUM = self::P_CREATE_CHANNEL | self::P_SET_CHAN_PERMA | self::P_SET_CHAN_PASS
|
||||
| self::P_SET_CHAN_HIER | self::P_DELETE_CHANNEL | self::P_JOIN_ANY_CHAN;
|
||||
|
||||
public static function convert(User $userInfo): int {
|
||||
$perms = self::PERMS_DEFAULT;
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_MANAGE_USERS))
|
||||
$perms |= self::PERMS_MANAGE_USERS;
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_MANAGE_WARNINGS))
|
||||
$perms |= self::PERMS_MANAGE_WARNS;
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_CHANGE_BACKGROUND))
|
||||
$perms |= self::PERMS_CHANGE_BACKG;
|
||||
if(perms_check_user(MSZ_PERMS_FORUM, $userInfo->getId(), MSZ_PERM_FORUM_MANAGE_FORUMS))
|
||||
$perms |= self::PERMS_MANAGE_FORUM;
|
||||
|
||||
return $perms;
|
||||
}
|
||||
}
|
|
@ -1,238 +1,86 @@
|
|||
<?php
|
||||
namespace Misuzu\Http\Handlers;
|
||||
namespace Misuzu\SharpChat;
|
||||
|
||||
use Misuzu\MszContext;
|
||||
use Index\Http\HttpFx; // this should be replaced with an interface that implements the routes stuff
|
||||
|
||||
// Replace
|
||||
use Misuzu\AuthToken;
|
||||
use Misuzu\Config;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Emoticon;
|
||||
use Misuzu\Stream;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\UserSessionNotFoundException;
|
||||
use Misuzu\Users\UserWarning;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserWarningCreationFailedException;
|
||||
use Index\Http\HttpRequestBuilder;
|
||||
|
||||
final class SockChatHandler extends Handler {
|
||||
final class SharpChatRoutes {
|
||||
private string $hashKey = 'woomy';
|
||||
|
||||
private const P_KICK_USER = 0x00000001;
|
||||
private const P_BAN_USER = 0x00000002;
|
||||
private const P_SILENCE_USER = 0x00000004;
|
||||
private const P_BROADCAST = 0x00000008;
|
||||
private const P_SET_OWN_NICK = 0x00000010;
|
||||
private const P_SET_OTHER_NICK = 0x00000020;
|
||||
private const P_CREATE_CHANNEL = 0x00000040;
|
||||
private const P_DELETE_CHANNEL = 0x00010000;
|
||||
private const P_SET_CHAN_PERMA = 0x00000080;
|
||||
private const P_SET_CHAN_PASS = 0x00000100;
|
||||
private const P_SET_CHAN_HIER = 0x00000200;
|
||||
private const P_JOIN_ANY_CHAN = 0x00020000;
|
||||
private const P_SEND_MESSAGE = 0x00000400;
|
||||
private const P_DELETE_OWN_MSG = 0x00000800;
|
||||
private const P_DELETE_ANY_MSG = 0x00001000;
|
||||
private const P_EDIT_OWN_MSG = 0x00002000;
|
||||
private const P_EDIT_ANY_MSG = 0x00004000;
|
||||
private const P_VIEW_IP_ADDR = 0x00008000;
|
||||
|
||||
private const PERMS_DEFAULT = self::P_SEND_MESSAGE | self::P_DELETE_OWN_MSG | self::P_EDIT_OWN_MSG;
|
||||
private const PERMS_MANAGE_USERS = self::P_SET_OWN_NICK | self::P_SET_OTHER_NICK | self::P_DELETE_ANY_MSG
|
||||
| self::P_EDIT_ANY_MSG | self::P_VIEW_IP_ADDR | self::P_BROADCAST;
|
||||
private const PERMS_MANAGE_WARNS = self::P_KICK_USER | self::P_BAN_USER | self::P_SILENCE_USER;
|
||||
private const PERMS_CHANGE_BACKG = self::P_SET_OWN_NICK | self::P_CREATE_CHANNEL | self::P_SET_CHAN_PASS;
|
||||
private const PERMS_MANAGE_FORUM = self::P_CREATE_CHANNEL | self::P_SET_CHAN_PERMA | self::P_SET_CHAN_PASS
|
||||
| self::P_SET_CHAN_HIER | self::P_DELETE_CHANNEL | self::P_JOIN_ANY_CHAN;
|
||||
|
||||
public function __construct() {
|
||||
public function __construct(MszContext $ctx) {
|
||||
$hashKeyPath = Config::get('sockChat.hashKeyPath', Config::TYPE_STR, '');
|
||||
|
||||
if(is_file($hashKeyPath))
|
||||
$this->hashKey = file_get_contents($hashKeyPath);
|
||||
|
||||
$GLOBALS['misuzuBypassLockdown'] = true;
|
||||
parent::__construct();
|
||||
$router = $ctx->getRouter();
|
||||
|
||||
// Public endpoints
|
||||
$router->get('/_sockchat/emotes', [$this, 'emotes']);
|
||||
$router->get('/_sockchat/login', [$this, 'login']);
|
||||
$router->options('/_sockchat/token', [$this, 'token']);
|
||||
$router->get('/_sockchat/token', [$this, 'token']);
|
||||
|
||||
// Private endpoints
|
||||
$router->get('/_sockchat/resolve', [$this, 'resolve']);
|
||||
$router->post('/_sockchat/bump', [$this, 'bump']);
|
||||
$router->post('/_sockchat/verify', [$this, 'verify']);
|
||||
$router->get('/_sockchat/bans', [$this, 'bans']);
|
||||
$router->get('/_sockchat/bans/check', [$this, 'checkBan']);
|
||||
$router->post('/_sockchat/bans/create', [$this, 'createBan']);
|
||||
$router->delete('/_sockchat/bans/remove', [$this, 'removeBan']);
|
||||
}
|
||||
|
||||
public static function calculatePermissions(User $userInfo): int {
|
||||
$perms = self::PERMS_DEFAULT;
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_MANAGE_USERS))
|
||||
$perms |= self::PERMS_MANAGE_USERS;
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_MANAGE_WARNINGS))
|
||||
$perms |= self::PERMS_MANAGE_WARNS;
|
||||
if(perms_check_user(MSZ_PERMS_USER, $userInfo->getId(), MSZ_PERM_USER_CHANGE_BACKGROUND))
|
||||
$perms |= self::PERMS_CHANGE_BACKG;
|
||||
if(perms_check_user(MSZ_PERMS_FORUM, $userInfo->getId(), MSZ_PERM_FORUM_MANAGE_FORUMS))
|
||||
$perms |= self::PERMS_MANAGE_FORUM;
|
||||
|
||||
return $perms;
|
||||
}
|
||||
|
||||
public function emotes($response, $request): array {
|
||||
public static function emotes($response, $request): array {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'GET');
|
||||
|
||||
$version = 1;//(int)$request->getParam('v', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$raw = Emoticon::all();
|
||||
$out = [];
|
||||
|
||||
foreach($raw as $emote) {
|
||||
$strings = [];
|
||||
if($version >= 2) {
|
||||
foreach($raw as $emote) {
|
||||
$strings = [];
|
||||
|
||||
foreach($emote->getStrings() as $string) {
|
||||
$strings[] = sprintf(':%s:', $string->emote_string);
|
||||
foreach($emote->getStrings() as $string)
|
||||
$strings[] = $string->emote_string;
|
||||
|
||||
$out[] = [
|
||||
's' => $strings,
|
||||
'u' => $emote->getUrl(),
|
||||
'r' => $emote->getRank(),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
foreach($raw as $emote) {
|
||||
$strings = [];
|
||||
|
||||
$out[] = [
|
||||
'Text' => $strings,
|
||||
'Image' => $emote->getUrl(),
|
||||
'Hierarchy' => $emote->getRank(),
|
||||
];
|
||||
foreach($emote->getStrings() as $string)
|
||||
$strings[] = sprintf(':%s:', $string->emote_string);
|
||||
|
||||
$out[] = [
|
||||
'Text' => $strings,
|
||||
'Image' => $emote->getUrl(),
|
||||
'Hierarchy' => $emote->getRank(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function bans($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$realHash = hash_hmac('sha256', 'givemethebeans', $this->hashKey);
|
||||
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return [];
|
||||
|
||||
$warnings = UserWarning::byActive();
|
||||
$bans = [];
|
||||
|
||||
foreach($warnings as $warning) {
|
||||
if(!$warning->isBan() || $warning->hasExpired())
|
||||
continue;
|
||||
|
||||
$isPermanent = $warning->isPermanent();
|
||||
$userInfo = $warning->getUser();
|
||||
$bans[] = [
|
||||
'user_id' => $userInfo->getId(),
|
||||
'id' => $userInfo->getId(),
|
||||
'username' => $userInfo->getUsername(),
|
||||
'colour_raw' => $userInfo->getColour()->getRaw(),
|
||||
'rank' => $rank = $userInfo->getRank(),
|
||||
'ip' => $warning->getUserRemoteAddress(),
|
||||
'is_permanent' => $isPermanent,
|
||||
'expires' => date('c', $isPermanent ? 0x7FFFFFFF : $warning->getExpirationTime()),
|
||||
'perms' => self::calculatePermissions($userInfo),
|
||||
];
|
||||
}
|
||||
|
||||
return $bans;
|
||||
}
|
||||
|
||||
public function checkBan($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$ipAddress = (string)$request->getParam('a');
|
||||
$userId = (int)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$realHash = hash_hmac('sha256', "check#{$ipAddress}#{$userId}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return [];
|
||||
|
||||
$response = [];
|
||||
$warning = UserWarning::byRemoteAddressActive($ipAddress)
|
||||
?? UserWarning::byUserIdActive($userId);
|
||||
|
||||
if($warning !== null) {
|
||||
$response['warning'] = $warning->getId();
|
||||
$response['id'] = $warning->getUserId();
|
||||
$response['user_id'] = $warning->getUserId();
|
||||
$response['ip'] = $warning->getUserRemoteAddress();
|
||||
$response['is_permanent'] = $warning->isPermanent();
|
||||
$response['expires'] = date('c', $response['is_permanent'] ? 0x7FFFFFFF : $warning->getExpirationTime());
|
||||
} else {
|
||||
$response['expires'] = date('c', 0);
|
||||
$response['is_permanent'] = false;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function createBan($response, $request): int {
|
||||
if(!$request->isFormContent())
|
||||
return 400;
|
||||
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$content = $request->getContent();
|
||||
$userId = (int)$content->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$modId = (int)$content->getParam('m', FILTER_SANITIZE_NUMBER_INT);
|
||||
$duration = (int)$content->getParam('d', FILTER_SANITIZE_NUMBER_INT);
|
||||
$isPermanent = (int)$content->getParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$reason = (string)$content->getParam('r');
|
||||
|
||||
$realHash = hash_hmac('sha256', "create#{$userId}#{$modId}#{$duration}#{$isPermanent}#{$reason}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return 403;
|
||||
|
||||
if(empty($reason))
|
||||
$reason = 'Banned through chat.';
|
||||
|
||||
if($isPermanent)
|
||||
$duration = -1;
|
||||
elseif($duration < 1)
|
||||
return 400;
|
||||
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
try {
|
||||
$modInfo = User::byId($modId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
try {
|
||||
UserWarning::create(
|
||||
$userInfo,
|
||||
$modInfo,
|
||||
UserWarning::TYPE_BAHN,
|
||||
$duration,
|
||||
$reason
|
||||
);
|
||||
} catch(UserWarningCreationFailedException $ex) {
|
||||
return 500;
|
||||
}
|
||||
|
||||
return 201;
|
||||
}
|
||||
|
||||
public function removeBan($response, $request): int {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$type = (string)$request->getParam('t');
|
||||
$subject = (string)$request->getParam('s');
|
||||
|
||||
$realHash = hash_hmac('sha256', "remove#{$type}#{$subject}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return 403;
|
||||
|
||||
$warning = null;
|
||||
switch($type) {
|
||||
case 'ip':
|
||||
$warning = UserWarning::byRemoteAddressActive($subject);
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
$warning = UserWarning::byUserIdActive((int)$subject);
|
||||
break;
|
||||
}
|
||||
|
||||
if($warning === null)
|
||||
return 404;
|
||||
|
||||
$warning->delete();
|
||||
|
||||
return 204;
|
||||
}
|
||||
|
||||
public function login($response, $request) {
|
||||
public static function login($response, $request): void {
|
||||
$currentUser = User::getCurrent();
|
||||
$configKey = $request->hasParam('legacy') ? 'sockChat.chatPath.legacy' : 'sockChat.chatPath.normal';
|
||||
$chatPath = Config::get($configKey, Config::TYPE_STR, '/');
|
||||
|
@ -244,11 +92,83 @@ final class SockChatHandler extends Handler {
|
|||
);
|
||||
}
|
||||
|
||||
public function token($response, $request) {
|
||||
$host = $request->hasHeader('Host') ? $request->getHeaderFirstLine('Host') : '';
|
||||
$origin = $request->hasHeader('Origin') ? $request->getHeaderFirstLine('Origin') : '';
|
||||
$originHost = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
|
||||
if(!empty($originHost) && $originHost !== $host) {
|
||||
$whitelist = Config::get('sockChat.origins', Config::TYPE_ARR, []);
|
||||
|
||||
if(!in_array($originHost, $whitelist))
|
||||
return 403;
|
||||
|
||||
$originProto = strtolower(parse_url($origin, PHP_URL_SCHEME));
|
||||
$origin = $originProto . '://' . $originHost;
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $origin);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
|
||||
$response->setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
$response->setHeader('Vary', 'Origin');
|
||||
}
|
||||
|
||||
if($request->getMethod() === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
if(!UserSession::hasCurrent())
|
||||
return ['ok' => false];
|
||||
|
||||
$session = UserSession::getCurrent();
|
||||
$user = $session->getUser();
|
||||
$token = AuthToken::create($user, $session);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'usr' => $user->getId(),
|
||||
'tkn' => $token->pack(),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolve($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$method = (string)$request->getParam('m');
|
||||
$param = (string)$request->getParam('p');
|
||||
$realHash = hash_hmac('sha256', "resolve#{$method}#{$param}", $this->hashKey);
|
||||
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return [];
|
||||
|
||||
try {
|
||||
switch($method) {
|
||||
case 'id':
|
||||
$userInfo = User::byId((int)$param);
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
$userInfo = User::byUsername($param);
|
||||
break;
|
||||
}
|
||||
} catch(UserNotFoundException $ex) {}
|
||||
|
||||
if(!isset($userInfo))
|
||||
return [];
|
||||
|
||||
return [
|
||||
'user_id' => $userInfo->getId(),
|
||||
'username' => $userInfo->getUsername(),
|
||||
'colour_raw' => $userInfo->getColour()->getRaw(),
|
||||
'rank' => $rank = $userInfo->getRank(),
|
||||
'perms' => SharpChatPerms::convert($userInfo),
|
||||
];
|
||||
}
|
||||
|
||||
public function bump($response, $request) {
|
||||
if(!$request->isStringContent())
|
||||
return 400;
|
||||
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$bumpString = (string)$request->getContent();
|
||||
$realHash = hash_hmac('sha256', $bumpString, $this->hashKey);
|
||||
|
||||
|
@ -274,7 +194,8 @@ final class SockChatHandler extends Handler {
|
|||
else
|
||||
return ['success' => false, 'reason' => 'request'];
|
||||
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
|
||||
if(strlen($userHash) !== 64)
|
||||
return ['success' => false, 'reason' => 'length'];
|
||||
|
@ -331,158 +252,150 @@ final class SockChatHandler extends Handler {
|
|||
'rank' => $rank = $userInfo->getRank(),
|
||||
'hierarchy' => $rank,
|
||||
'is_silenced' => date('c', $userInfo->isSilenced() || $userInfo->isBanned() ? ($userInfo->isActiveWarningPermanent() ? strtotime('10 years') : $userInfo->getActiveWarningExpiration()) : 0),
|
||||
'perms' => self::calculatePermissions($userInfo),
|
||||
'perms' => SharpChatPerms::convert($userInfo),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolve($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature') ? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$method = (string)$request->getParam('m');
|
||||
$param = (string)$request->getParam('p');
|
||||
$realHash = hash_hmac('sha256', "resolve#{$method}#{$param}", $this->hashKey);
|
||||
public function bans($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$realHash = hash_hmac('sha256', 'givemethebeans', $this->hashKey);
|
||||
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return [];
|
||||
|
||||
try {
|
||||
switch($method) {
|
||||
case 'id':
|
||||
$userInfo = User::byId((int)$param);
|
||||
break;
|
||||
$warnings = UserWarning::byActive();
|
||||
$bans = [];
|
||||
|
||||
case 'name':
|
||||
$userInfo = User::byUsername($param);
|
||||
break;
|
||||
}
|
||||
} catch(UserNotFoundException $ex) {}
|
||||
foreach($warnings as $warning) {
|
||||
if(!$warning->isBan() || $warning->hasExpired())
|
||||
continue;
|
||||
|
||||
if(!isset($userInfo))
|
||||
$isPermanent = $warning->isPermanent();
|
||||
$userInfo = $warning->getUser();
|
||||
$bans[] = [
|
||||
'user_id' => $userInfo->getId(),
|
||||
'id' => $userInfo->getId(),
|
||||
'username' => $userInfo->getUsername(),
|
||||
'colour_raw' => $userInfo->getColour()->getRaw(),
|
||||
'rank' => $rank = $userInfo->getRank(),
|
||||
'ip' => $warning->getUserRemoteAddress(),
|
||||
'is_permanent' => $isPermanent,
|
||||
'expires' => date('c', $isPermanent ? 0x7FFFFFFF : $warning->getExpirationTime()),
|
||||
'perms' => SharpChatPerms::convert($userInfo),
|
||||
];
|
||||
}
|
||||
|
||||
return $bans;
|
||||
}
|
||||
|
||||
public function checkBan($response, $request): array {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$ipAddress = (string)$request->getParam('a');
|
||||
$userId = (int)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$realHash = hash_hmac('sha256', "check#{$ipAddress}#{$userId}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return [];
|
||||
|
||||
return [
|
||||
'user_id' => $userInfo->getId(),
|
||||
'username' => $userInfo->getUsername(),
|
||||
'colour_raw' => $userInfo->getColour()->getRaw(),
|
||||
'rank' => $rank = $userInfo->getRank(),
|
||||
'perms' => self::calculatePermissions($userInfo),
|
||||
];
|
||||
}
|
||||
$response = [];
|
||||
$warning = UserWarning::byRemoteAddressActive($ipAddress)
|
||||
?? UserWarning::byUserIdActive($userId);
|
||||
|
||||
public function token($response, $request) {
|
||||
$host = $request->hasHeader('Host') ? $request->getHeaderFirstLine('Host') : '';
|
||||
$origin = $request->hasHeader('Origin') ? $request->getHeaderFirstLine('Origin') : '';
|
||||
$originHost = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
|
||||
if(!empty($originHost) && $originHost !== $host) {
|
||||
$whitelist = Config::get('sockChat.origins', Config::TYPE_ARR, []);
|
||||
|
||||
if(!in_array($originHost, $whitelist))
|
||||
return 403;
|
||||
|
||||
$originProto = strtolower(parse_url($origin, PHP_URL_SCHEME));
|
||||
$origin = $originProto . '://' . $originHost;
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $origin);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
|
||||
$response->setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
$response->setHeader('Vary', 'Origin');
|
||||
if($warning !== null) {
|
||||
$response['warning'] = $warning->getId();
|
||||
$response['id'] = $warning->getUserId();
|
||||
$response['user_id'] = $warning->getUserId();
|
||||
$response['ip'] = $warning->getUserRemoteAddress();
|
||||
$response['is_permanent'] = $warning->isPermanent();
|
||||
$response['expires'] = date('c', $response['is_permanent'] ? 0x7FFFFFFF : $warning->getExpirationTime());
|
||||
} else {
|
||||
$response['expires'] = date('c', 0);
|
||||
$response['is_permanent'] = false;
|
||||
}
|
||||
|
||||
if($request->getMethod() === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
if(!UserSession::hasCurrent())
|
||||
return ['ok' => false];
|
||||
|
||||
$session = UserSession::getCurrent();
|
||||
$user = $session->getUser();
|
||||
$token = AuthToken::create($user, $session);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'usr' => $user->getId(),
|
||||
'tkn' => $token->pack(),
|
||||
];
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function profileCheck($response, $request) {
|
||||
$host = $request->hasHeader('Host') ? $request->getHeaderFirstLine('Host') : '';
|
||||
$origin = $request->hasHeader('Origin') ? $request->getHeaderFirstLine('Origin') : '';
|
||||
$originHost = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
public function createBan($response, $request): int {
|
||||
if(!$request->isFormContent())
|
||||
return 400;
|
||||
|
||||
if(!empty($originHost) && $originHost !== $host) {
|
||||
$whitelist = Config::get('sockChat.origins', Config::TYPE_ARR, []);
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$content = $request->getContent();
|
||||
$userId = (int)$content->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$modId = (int)$content->getParam('m', FILTER_SANITIZE_NUMBER_INT);
|
||||
$duration = (int)$content->getParam('d', FILTER_SANITIZE_NUMBER_INT);
|
||||
$isPermanent = (int)$content->getParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$reason = (string)$content->getParam('r');
|
||||
|
||||
if(!in_array($originHost, $whitelist))
|
||||
return 403;
|
||||
$realHash = hash_hmac('sha256', "create#{$userId}#{$modId}#{$duration}#{$isPermanent}#{$reason}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return 403;
|
||||
|
||||
$originProto = strtolower(parse_url($origin, PHP_URL_SCHEME));
|
||||
$origin = $originProto . '://' . $originHost;
|
||||
if(empty($reason))
|
||||
$reason = 'Banned through chat.';
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $origin);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
|
||||
$response->setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
$response->setHeader('Vary', 'Origin');
|
||||
}
|
||||
if($isPermanent)
|
||||
$duration = -1;
|
||||
elseif($duration < 1)
|
||||
return 400;
|
||||
|
||||
if($request->getMethod() === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
$userId = (int)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$extendedInfo = $request->hasParam('e');
|
||||
|
||||
if($userId < 1)
|
||||
$userInfo = User::getCurrent();
|
||||
else {
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$response->setStatusCode(404);
|
||||
return ['is_ok' => false];
|
||||
}
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
try {
|
||||
$hasIntro = (bool)DB::prepare('SELECT COUNT(*) > 0 FROM `msz_forum_topics` WHERE `forum_id` = 6 AND `user_id` = :user AND `topic_deleted` IS NULL')
|
||||
->bind('user', $userInfo->getId())
|
||||
->fetchColumn();
|
||||
} catch(\PDOException $ex) {
|
||||
$hasIntro = false;
|
||||
$modInfo = User::byId($modId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
$isOld = $userInfo->getCreatedTime() < strtotime('1 year ago');
|
||||
$hasAvatar = $userInfo->hasAvatar();
|
||||
$hasAbout = $userInfo->hasProfileAbout();
|
||||
$hasLinks = !empty($userInfo->profileFields());
|
||||
$isOk = $isOld;
|
||||
|
||||
if(!$isOk) {
|
||||
$points = 0;
|
||||
if($hasAvatar)
|
||||
++$points;
|
||||
if($hasAbout)
|
||||
++$points;
|
||||
if($hasLinks)
|
||||
++$points;
|
||||
if($hasIntro)
|
||||
++$points;
|
||||
$isOk = $points >= ($userInfo->getCreatedTime() < strtotime('1 month ago') ? 1 : 2);
|
||||
try {
|
||||
UserWarning::create(
|
||||
$userInfo,
|
||||
$modInfo,
|
||||
UserWarning::TYPE_BAHN,
|
||||
$duration,
|
||||
$reason
|
||||
);
|
||||
} catch(UserWarningCreationFailedException $ex) {
|
||||
return 500;
|
||||
}
|
||||
|
||||
$res = [
|
||||
'user_id' => $userInfo->getId(),
|
||||
'is_ok' => $isOk,
|
||||
];
|
||||
return 201;
|
||||
}
|
||||
|
||||
if($extendedInfo) {
|
||||
$res['username'] = $userInfo->getUsername();
|
||||
$res['is_old'] = $isOld;
|
||||
$res['has_avatar'] = $hasAvatar;
|
||||
$res['has_about'] = $hasAbout;
|
||||
$res['has_links'] = $hasLinks;
|
||||
$res['has_intro'] = $hasIntro;
|
||||
public function removeBan($response, $request): int {
|
||||
$userHash = $request->hasHeader('X-SharpChat-Signature')
|
||||
? $request->getHeaderFirstLine('X-SharpChat-Signature') : '';
|
||||
$type = (string)$request->getParam('t');
|
||||
$subject = (string)$request->getParam('s');
|
||||
|
||||
$realHash = hash_hmac('sha256', "remove#{$type}#{$subject}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return 403;
|
||||
|
||||
$warning = null;
|
||||
switch($type) {
|
||||
case 'ip':
|
||||
$warning = UserWarning::byRemoteAddressActive($subject);
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
$warning = UserWarning::byUserIdActive((int)$subject);
|
||||
break;
|
||||
}
|
||||
|
||||
return $res;
|
||||
if($warning === null)
|
||||
return 404;
|
||||
|
||||
$warning->delete();
|
||||
|
||||
return 204;
|
||||
}
|
||||
}
|
43
src/Users/Users.php
Normal file
43
src/Users/Users.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Misuzu\Database\Database;
|
||||
|
||||
final class Users {
|
||||
private Database $db;
|
||||
|
||||
private array $cached = [];
|
||||
|
||||
public function __construct(Database $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getById(string $userId): User {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByName(string $userName): User {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByMailAddress(string $mailAddress): User {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByNameOrMailAddress(string $userNameOrMailAddress): User {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByIdOrName(string $userIdOrName): User {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByBirthDate(DateTimeImmutable $dateTime): array {
|
||||
//
|
||||
}
|
||||
|
||||
public function getByAll(bool $includeDeleted = false, ?Pagination $pagination = null): array {
|
||||
//
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue