Added ban management endpoints for the chat.
This commit is contained in:
parent
b7ff4f6a6e
commit
71e905c9a8
4 changed files with 138 additions and 4 deletions
|
@ -53,10 +53,14 @@ Router::addRoutes(
|
||||||
Route::create(['GET', 'POST'], '/_sockchat.php', 'phpFile', 'SockChat'),
|
Route::create(['GET', 'POST'], '/_sockchat.php', 'phpFile', 'SockChat'),
|
||||||
Route::group('/_sockchat', 'SockChat')->addChildren(
|
Route::group('/_sockchat', 'SockChat')->addChildren(
|
||||||
Route::get('/emotes', 'emotes'),
|
Route::get('/emotes', 'emotes'),
|
||||||
Route::get('/bans', 'bans'),
|
|
||||||
Route::get('/login', 'login'),
|
Route::get('/login', 'login'),
|
||||||
Route::post('/bump', 'bump'),
|
Route::post('/bump', 'bump'),
|
||||||
Route::post('/verify', 'verify'),
|
Route::post('/verify', 'verify'),
|
||||||
|
Route::get('/bans', 'bans')->addChildren(
|
||||||
|
Route::get('/check', 'checkBan'),
|
||||||
|
Route::post('/create', 'createBan'),
|
||||||
|
Route::delete('/remove', 'removeBan'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Redirects
|
// Redirects
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Misuzu\Users\UserChatTokenCreationFailedException;
|
||||||
use Misuzu\Users\UserSession;
|
use Misuzu\Users\UserSession;
|
||||||
use Misuzu\Users\UserSessionNotFoundException;
|
use Misuzu\Users\UserSessionNotFoundException;
|
||||||
use Misuzu\Users\UserWarning;
|
use Misuzu\Users\UserWarning;
|
||||||
|
use Misuzu\Users\UserWarningCreationFailedException;
|
||||||
|
|
||||||
final class SockChatHandler extends Handler {
|
final class SockChatHandler extends Handler {
|
||||||
private string $hashKey = 'woomy';
|
private string $hashKey = 'woomy';
|
||||||
|
@ -132,17 +133,127 @@ final class SockChatHandler extends Handler {
|
||||||
if(!$warning->isBan() || $warning->hasExpired())
|
if(!$warning->isBan() || $warning->hasExpired())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
$isPermanent = $warning->isPermanent();
|
||||||
$bans[] = [
|
$bans[] = [
|
||||||
'id' => $warning->getUser()->getId(),
|
'id' => $warning->getUser()->getId(),
|
||||||
'expires' => date('c', $warning->isPermanent() ? 0x7FFFFFFF : $warning->getExpirationTime()),
|
'expires' => date('c', $isPermanent ? 0x7FFFFFFF : $warning->getExpirationTime()),
|
||||||
|
'is_permanent' => $isPermanent,
|
||||||
'ip' => $warning->getUserRemoteAddress(),
|
'ip' => $warning->getUserRemoteAddress(),
|
||||||
'username' => $warning->getUser()->getUsername()
|
'username' => $warning->getUser()->getUsername(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $bans;
|
return $bans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkBan(HttpResponse $response, HttpRequest $request): array {
|
||||||
|
$userHash = $request->getHeaderLine('X-SharpChat-Signature');
|
||||||
|
$ipAddress = (string)$request->getQueryParam('a', FILTER_SANITIZE_STRING);
|
||||||
|
$userId = (int)$request->getQueryParam('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['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(HttpResponse $response, HttpRequest $request): int {
|
||||||
|
$userHash = $request->getHeaderLine('X-SharpChat-Signature');
|
||||||
|
$userId = (int)$request->getBodyParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$modId = (int)$request->getBodyParam('m', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$duration = (int)$request->getBodyParam('d', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$isPermanent = (int)$request->getBodyParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$reason = (string)$request->getBodyParam('r', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
$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(HttpResponse $response, HttpRequest $request): int {
|
||||||
|
$userHash = $request->getHeaderLine('X-SharpChat-Signature');
|
||||||
|
$type = (string)$request->getQueryParam('t', FILTER_SANITIZE_STRING);
|
||||||
|
$subject = (string)$request->getQueryParam('s', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
$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':
|
||||||
|
try {
|
||||||
|
$userInfo = User::byUsername($subject);
|
||||||
|
} catch(UserNotFoundException $ex) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
$warning = UserWarning::byUserIdActive($userInfo->getId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($warning === null)
|
||||||
|
return 404;
|
||||||
|
|
||||||
|
$warning->delete();
|
||||||
|
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
public function login(HttpResponse $response, HttpRequest $request) {
|
public function login(HttpResponse $response, HttpRequest $request) {
|
||||||
$currentUser = User::getCurrent();
|
$currentUser = User::getCurrent();
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ class Route implements Serializable {
|
||||||
public static function post(string $path, ?string $method = null, ?string $class = null): self {
|
public static function post(string $path, ?string $method = null, ?string $class = null): self {
|
||||||
return self::create(['POST'], $path, $method, $class);
|
return self::create(['POST'], $path, $method, $class);
|
||||||
}
|
}
|
||||||
|
public static function delete(string $path, ?string $method = null, ?string $class = null): self {
|
||||||
|
return self::create(['DELETE'], $path, $method, $class);
|
||||||
|
}
|
||||||
public static function group(string $path, ?string $class = null): self {
|
public static function group(string $path, ?string $class = null): self {
|
||||||
return self::create([''], $path, null, $class);
|
return self::create([''], $path, null, $class);
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,13 +228,29 @@ class UserWarning {
|
||||||
return $object;
|
return $object;
|
||||||
}
|
}
|
||||||
public static function byUserActive(User $user): ?self {
|
public static function byUserActive(User $user): ?self {
|
||||||
|
return self::byUserIdActive($user->getId());
|
||||||
|
}
|
||||||
|
public static function byUserIdActive(int $userId): ?self {
|
||||||
|
if($userId < 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
return DB::prepare(
|
return DB::prepare(
|
||||||
self::byQueryBase()
|
self::byQueryBase()
|
||||||
. ' WHERE `user_id` = :user'
|
. ' WHERE `user_id` = :user'
|
||||||
. ' AND `warning_type` IN (' . implode(',', self::HAS_DURATION) . ')'
|
. ' AND `warning_type` IN (' . implode(',', self::HAS_DURATION) . ')'
|
||||||
. ' AND (`warning_duration` IS NULL OR `warning_duration` >= NOW())'
|
. ' AND (`warning_duration` IS NULL OR `warning_duration` >= NOW())'
|
||||||
. ' ORDER BY `warning_type` DESC, `warning_duration` DESC'
|
. ' ORDER BY `warning_type` DESC, `warning_duration` DESC'
|
||||||
) ->bind('user', $user->getId())
|
) ->bind('user', $userId)
|
||||||
|
->fetchObject(self::class);
|
||||||
|
}
|
||||||
|
public static function byRemoteAddressActive(string $ipAddress): ?self {
|
||||||
|
return DB::prepare(
|
||||||
|
self::byQueryBase()
|
||||||
|
. ' WHERE `user_ip` = INET6_ATON(:address)'
|
||||||
|
. ' AND `warning_type` IN (' . implode(',', self::HAS_DURATION) . ')'
|
||||||
|
. ' AND (`warning_duration` IS NULL OR `warning_duration` >= NOW())'
|
||||||
|
. ' ORDER BY `warning_type` DESC, `warning_duration` DESC'
|
||||||
|
) ->bind('address', $ipAddress)
|
||||||
->fetchObject(self::class);
|
->fetchObject(self::class);
|
||||||
}
|
}
|
||||||
public static function byProfile(User $user, ?User $viewer = null): array {
|
public static function byProfile(User $user, ?User $viewer = null): array {
|
||||||
|
|
Loading…
Add table
Reference in a new issue