Converted Sharp Chat routes to Index.

This commit is contained in:
flash 2022-06-10 20:26:24 +02:00
parent 6e650923d8
commit 7d6b94294f
5 changed files with 350 additions and 342 deletions

View file

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

View file

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

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

View file

@ -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
View 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 {
//
}
}