Replaced internal Flashii ID routes with RPC library.

This commit is contained in:
flash 2024-08-16 19:29:57 +00:00
parent cc9fccdf18
commit 0bf7ca0d52
4 changed files with 78 additions and 81 deletions

View file

@ -8,7 +8,8 @@
"symfony/mailer": "^6.0",
"matomo/device-detector": "^6.1",
"sentry/sdk": "^4.0",
"nesbot/carbon": "^3.7"
"nesbot/carbon": "^3.7",
"flashwave/aiwass": "^1.0"
},
"autoload": {
"classmap": [

41
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cec099ea3b4fce65aa1b1ff6b0b40f24",
"content-hash": "c8edfa21e13dedc126a6351b7d367559",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@ -416,6 +416,45 @@
},
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "flashwave/aiwass",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://patchii.net/flashii/aiwass.git",
"reference": "de27da54b603f0fdc06dab89341908e73ea7a880"
},
"require": {
"ext-msgpack": ">=2.2",
"flashwave/index": "^0.2408.40014",
"php": ">=8.3"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^11.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Aiwass\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"bsd-3-clause-clear"
],
"authors": [
{
"name": "flashwave",
"email": "packagist@flash.moe",
"homepage": "https://flash.moe",
"role": "mom"
}
],
"description": "Shared HTTP RPC client/server library.",
"homepage": "https://railgun.sh/aiwass",
"time": "2024-08-16T15:59:19+00:00"
},
{
"name": "flashwave/index",
"version": "v0.2408.40014",

View file

@ -1,42 +1,30 @@
<?php
namespace Misuzu\Hanyuu;
use stdClass;
use RuntimeException;
use Index\UriBase64;
use Index\Colour\Colour;
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler};
use Syokuhou\IConfig;
use Misuzu\CSRF;
use Misuzu\Auth\{AuthContext,AuthInfo,Sessions};
use Misuzu\Auth\AuthContext;
use Misuzu\URLs\URLRegistry;
use Misuzu\Users\{UsersContext,UserInfo};
use Aiwass\Server\{RpcActionHandler,RpcProcedure};
use Index\Colour\Colour;
use Syokuhou\IConfig;
final class HanyuuRoutes extends RouteHandler {
final class HanyuuRpcActions extends RpcActionHandler {
public function __construct(
private IConfig $config,
private IConfig $impersonateConfig, // this sucks lol
private $getBaseUrl,
private IConfig $impersonateConfig,
private URLRegistry $urls,
private UsersContext $usersCtx,
private AuthContext $authCtx
) {}
private function getEndpoint(): string {
return $this->config->getString('endpoint');
}
private function getSecret(): string {
return $this->config->getString('secret');
}
private static function createPayload(string $name, array $attrs = []): object {
$payload = new stdClass;
$payload->name = $name;
$payload->attrs = [];
private static function createPayload(string $name, array $attrs = []): array {
$payload = ['name' => $name, 'attrs' => []];
foreach($attrs as $name => $value) {
if($value === null)
continue;
$payload->attrs[(string)$name] = $value;
$payload['attrs'][(string)$name] = $value;
}
return $payload;
@ -50,42 +38,6 @@ final class HanyuuRoutes extends RouteHandler {
return self::createPayload('error', $attrs);
}
#[HttpMiddleware('/_hanyuu/(.*)')]
public function verifyRequest($response, $request, string $action) {
$userTime = (int)$request->getHeaderLine('X-Hanyuu-Timestamp');
$userHash = UriBase64::decode((string)$request->getHeaderLine('X-Hanyuu-Signature'));
$currentTime = time();
if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60)
return self::createErrorPayload('verification', 'Request verification failed.');
$url = sprintf('%s/_hanyuu/%s', $this->getEndpoint(), $action);
$params = $request->getParamString();
if($params !== '')
$url .= sprintf('?%s', $params);
if($request->getMethod() === 'POST') {
if(!$request->isFormContent())
return self::createErrorPayload('request', 'Request body is not in expect format.');
$body = $request->getContent()->getParamString();
} elseif($request->getMethod() !== 'GET') {
return self::createErrorPayload('request', 'Only GET and POST methods are allowed.');
} else {
$body = '';
}
$verifyHash = hash_hmac(
'sha256',
sprintf('[%s|%s|%s]', $userTime, $url, $body),
$this->getSecret(),
true
);
if(!hash_equals($verifyHash, $userHash))
return self::createErrorPayload('verification', 'Request verification failed.');
}
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
if($impersonator->isSuperUser())
return true;
@ -97,18 +49,15 @@ final class HanyuuRoutes extends RouteHandler {
);
}
#[HttpPost('/_hanyuu/auth-check')]
public function postCheckAuth($response, $request) {
$content = $request->getContent();
$method = (string)$content->getParam('method');
#[RpcProcedure('mszhau:authCheck')]
public function procAuthCheck(string $method, string $remoteAddr, string $token, string $avatars = '') {
if($method !== 'Misuzu')
return self::createErrorPayload('auth:check:method', 'Requested auth method is not supported.');
$remoteAddr = (string)$content->getParam('remote_addr');
if(filter_var($remoteAddr, FILTER_VALIDATE_IP) === false)
return self::createErrorPayload('auth:check:remote_addr', 'Provided remote address is not in a valid format.');
$avatarResolutions = trim((string)$content->getParam('avatars'));
$avatarResolutions = trim($avatars);
if($avatarResolutions === '') {
$avatarResolutions = [];
} else {
@ -121,11 +70,12 @@ final class HanyuuRoutes extends RouteHandler {
$avatarResolutions = array_unique($avatarResolutions);
}
$loginUrl = $this->getEndpoint() . $this->urls->format('auth-login');
$registerUrl = $this->getEndpoint() . $this->urls->format('auth-register');
$baseUrl = ($this->getBaseUrl)();
$loginUrl = $baseUrl . $this->urls->format('auth-login');
$registerUrl = $baseUrl . $this->urls->format('auth-register');
$tokenPacker = $this->authCtx->createAuthTokenPacker();
$tokenInfo = $tokenPacker->unpack(trim((string)$content->getParam('token')));
$tokenInfo = $tokenPacker->unpack(trim($token));
if($tokenInfo->isEmpty())
return self::createPayload('auth:check:fail', ['reason' => 'empty', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
@ -173,9 +123,9 @@ final class HanyuuRoutes extends RouteHandler {
'remaining_str' => $banInfo->getRemainingString(),
];
$gatherRequestedAvatars = function($userInfo) use ($avatarResolutions) {
$gatherRequestedAvatars = function($userInfo) use ($avatarResolutions, $baseUrl) {
$formatAvatarUrl = fn($res = 0) => (
$this->getEndpoint() . $this->urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => $res])
$baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => $res])
);
$avatars = ['original' => $formatAvatarUrl()];
@ -195,7 +145,7 @@ final class HanyuuRoutes extends RouteHandler {
'country_code' => $userInfo->getCountryCode(),
'is_deleted' => $userInfo->isDeleted(),
'has_totp' => $userInfo->hasTOTPKey(),
'profile_url' => $this->getEndpoint() . $this->urls->format('user-profile', ['user' => $userInfo->getId()]),
'profile_url' => $baseUrl . $this->urls->format('user-profile', ['user' => $userInfo->getId()]),
'avatars' => $gatherRequestedAvatars($userInfo),
];
@ -204,7 +154,7 @@ final class HanyuuRoutes extends RouteHandler {
$response['guise'] = $extractUserInfo($userInfoReal);
$csrfp = CSRF::create($sessionInfo->getToken());
$response['guise']['revert_url'] = $this->getEndpoint() . $this->urls->format('auth-revert', ['csrf' => $csrfp->createToken()]);
$response['guise']['revert_url'] = $baseUrl . $this->urls->format('auth-revert', ['csrf' => $csrfp->createToken()]);
}
return self::createPayload('auth:check:success', $response);

View file

@ -1,10 +1,6 @@
<?php
namespace Misuzu;
use Index\Data\IDbConnection;
use Index\Data\Migration\{IDbMigrationRepo,DbMigrationManager,FsDbMigrationRepo};
use Sasae\SasaeEnvironment;
use Syokuhou\IConfig;
use Misuzu\Template;
use Misuzu\Auth\{AuthContext,AuthInfo};
use Misuzu\AuditLog\AuditLog;
@ -19,6 +15,12 @@ use Misuzu\Perms\Permissions;
use Misuzu\Profile\ProfileFields;
use Misuzu\URLs\URLRegistry;
use Misuzu\Users\{UsersContext,UserInfo};
use Aiwass\HmacVerificationProvider;
use Aiwass\Server\RpcServer;
use Index\Data\IDbConnection;
use Index\Data\Migration\{IDbMigrationRepo,DbMigrationManager,FsDbMigrationRepo};
use Sasae\SasaeEnvironment;
use Syokuhou\IConfig;
// this class should function as the root for everything going forward
// no more magical static classes that are just kind of assumed to exist
@ -277,16 +279,21 @@ class MisuzuContext {
$this->profileFields
));
$routingCtx->register(new \Misuzu\Hanyuu\HanyuuRoutes(
$this->config->scopeTo('hanyuu'),
$routingCtx->register(new LegacyRoutes($this->urls));
$rpcServer = new RpcServer;
$routingCtx->getRouter()->scopeTo('/_hanyuu')->register($rpcServer->createRouteHandler(
new HmacVerificationProvider(fn() => $this->config->getString('hanyuu.secret'))
));
$rpcServer->register(new Hanyuu\HanyuuRpcActions(
fn() => $this->config->getString('hanyuu.endpoint'),
$this->config->scopeTo('impersonate'),
$this->urls,
$this->usersCtx,
$this->authCtx
));
$routingCtx->register(new LegacyRoutes($this->urls));
return $routingCtx;
}
}