Removed RPCii dependency.

This commit is contained in:
flash 2025-03-25 14:27:50 +00:00
parent 6c50a582cb
commit 0b7031959b
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
10 changed files with 2 additions and 485 deletions

View file

@ -1 +1 @@
20250325.2
20250325.3

View file

@ -3,7 +3,6 @@
"php": ">=8.4",
"ext-mbstring": "*",
"flashwave/index": "^0.2503",
"flashii/rpcii": "~5.0",
"erusev/parsedown": "~1.7",
"chillerlan/php-qrcode": "~5.0",
"symfony/mailer": "~7.2",

43
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": "baa15cf491eb4500360fa9c20fd42a4d",
"content-hash": "cd96df7b2981a653c33b100a8b3837cd",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@ -499,47 +499,6 @@
],
"time": "2025-03-15T12:00:00+00:00"
},
{
"name": "flashii/rpcii",
"version": "v5.0.1",
"source": {
"type": "git",
"url": "https://patchii.net/flashii/rpcii-php.git",
"reference": "28c25e0a342173524f8894d03158663842e03252"
},
"require": {
"ext-msgpack": ">=2.2",
"flashwave/index": "^0.2503",
"guzzlehttp/guzzle": "~7.0",
"php": ">=8.4",
"psr/http-client": "^1.0"
},
"require-dev": {
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^12.0"
},
"type": "library",
"autoload": {
"psr-4": {
"RPCii\\": "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": "HTTP RPC client/server library.",
"homepage": "https://railgun.sh/rpcii",
"time": "2025-03-21T19:38:29+00:00"
},
{
"name": "flashwave/index",
"version": "v0.2503.251417",

View file

@ -12,7 +12,6 @@ Below are a number of links to source code repositories related to Flashii.net a
- [Uiharu](https://patchii.net/flashii/uiharu): Service for looking up URL metadata.
- [Seria](https://patchii.net/flashii/seria): Software used by the downloads tracker.
- [Mince](https://patchii.net/flashii/mince): Source code for the Minecraft servers subwebsite.
- [Aleister](https://patchii.net/flashii/aleister): Public API gateway.
## Tools & Software
- [SoFii](https://patchii.net/flashii/sofii): Launcher for Soldier of Fortune 2
@ -21,7 +20,6 @@ Below are a number of links to source code repositories related to Flashii.net a
## First-Party Libraries
- [Index](https://patchii.net/flash/index): Base library used in almost any component of the website that uses PHP.
- [RPCii](https://patchii.net/flashii/rpcii): Internal RPC extension, mainly used to supply data to the API gateway.
## Historical
- [AJAX Chat (fork)](https://patchii.net/flashii/ajax-chat): Old chat software (2013-2015). Still kept on life support for the nostalgia.

View file

@ -1,64 +0,0 @@
<?php
namespace Misuzu\Auth;
use RuntimeException;
use Misuzu\Users\{UsersContext,UserInfo};
use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcAction};
use Index\Config\Config;
final class AuthRpcHandler implements RpcHandler {
use RpcHandlerCommon;
public function __construct(
private Config $impersonateConfig,
private UsersContext $usersCtx,
private AuthContext $authCtx
) {}
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
if($impersonator->super)
return true;
$whitelist = $this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->id));
return in_array($targetId, $whitelist, true);
}
/** @return array{method: string, error: string}|array{method: string, type: string, user: string, expires: int} */
#[RpcAction('misuzu:auth:attemptMisuzuAuth')]
public function procAttemptMisuzuAuth(string $remoteAddr, string $token): array {
$tokenInfo = $this->authCtx->createAuthTokenPacker()->unpack($token);
if(!$tokenInfo->isEmpty)
$token = $tokenInfo->sessionToken;
try {
$sessionInfo = $this->authCtx->sessions->getSession(sessionToken: $token);
} catch(RuntimeException $ex) {
return ['method' => 'misuzu', 'error' => 'token'];
}
if($sessionInfo->expired) {
$this->authCtx->sessions->deleteSessions(sessionInfos: $sessionInfo);
return ['method' => 'misuzu', 'error' => 'expired'];
}
$this->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
$userInfo = $this->usersCtx->users->getUser($sessionInfo->userId, 'id');
if($tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId)) {
$userInfoReal = $userInfo;
try {
$userInfo = $this->usersCtx->users->getUser($tokenInfo->impersonatedUserId, 'id');
} catch(RuntimeException $ex) {
$userInfo = $userInfoReal;
}
}
return [
'method' => 'misuzu',
'type' => 'user',
'user' => $userInfo->id,
'expires' => $sessionInfo->expiresTime,
];
}
}

View file

@ -1,41 +0,0 @@
<?php
namespace Misuzu\Emoticons;
use Index\XArray;
use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcQuery};
final class EmotesRpcHandler implements RpcHandler {
use RpcHandlerCommon;
public function __construct(
private EmotesData $emotes
) {}
/** @return array<array{url: string, strings: string[], id?: string, order?: int, min_rank?: int}> */
#[RpcQuery('misuzu:emotes:all')]
public function queryAll(bool $includeId = false, bool $includeOrder = false): array {
return XArray::select(
$this->emotes->getEmotes(orderBy: 'order'),
function($emote) use ($includeId, $includeOrder) {
$info = [
'url' => $emote->url,
'strings' => XArray::select(
$this->emotes->getEmoteStrings($emote),
fn($string) => $string->string
),
];
if($includeId)
$info['id'] = $emote->id;
if($includeOrder)
$info['order'] = $emote->order;
$rank = $emote->minRank;
if($rank !== 0)
$info['min_rank'] = $rank;
return $info;
}
);
}
}

View file

@ -8,7 +8,6 @@ use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
use Misuzu\{Misuzu,Template};
use Misuzu\Parsers\{Parsers,TextFormat};
use RPCii\RPCii;
class InfoRoutes implements RouteHandler, UrlSource {
use RouteHandlerCommon, UrlSourceCommon;
@ -16,12 +15,10 @@ class InfoRoutes implements RouteHandler, UrlSource {
private const PROJECT_PATHS = [
'misuzu' => Misuzu::PATH_ROOT,
'index' => Index::PATH_ROOT,
'rpcii' => RPCii::PATH_ROOT,
];
private const PROJECT_SUFFIXES = [
'misuzu' => 'Misuzu » %s',
'index' => 'Index » %s',
'rpcii' => 'RPCii » %s',
];
#[ExactRoute('GET', '/info')]

View file

@ -11,8 +11,6 @@ use Index\Templating\TplEnvironment;
use Index\Urls\UrlRegistry;
use Misuzu\Routing\RoutingContext;
use Misuzu\Users\UserInfo;
use RPCii\HmacVerificationProvider;
use RPCii\Server\{HttpRpcServer,RpcServer};
class MisuzuContext {
public private(set) Dependencies $deps;
@ -158,11 +156,6 @@ class MisuzuContext {
public function registerMainRoutes(
RoutingContext $routingCtx
): void {
$rpcServer = new HttpRpcServer;
$routingCtx->register($rpcServer->createRouteHandler(
new HmacVerificationProvider(fn() => $this->config->getString('aleister.secret'))
));
$this->deps->register($wf = new WebFinger\WebFingerRegistry);
$wf->register($this->deps->constructLazy(
Users\UsersWebFingerResolver::class,
@ -190,7 +183,6 @@ class MisuzuContext {
$routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2ApiRoutes::class));
$routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2WebRoutes::class));
$rpcServer->register($this->deps->constructLazy(OAuth2\OAuth2RpcHandler::class));
$routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class));
$routingCtx->register($this->deps->constructLazy(Forum\ForumCategoriesRoutes::class));
@ -210,13 +202,6 @@ class MisuzuContext {
));
$routingCtx->register($this->deps->constructLazy(LegacyRoutes::class));
$rpcServer->register($this->deps->constructLazy(
Auth\AuthRpcHandler::class,
impersonateConfig: $this->config->scopeTo('impersonate')
));
$rpcServer->register($this->deps->constructLazy(Emoticons\EmotesRpcHandler::class));
$rpcServer->register($this->deps->constructLazy(Users\UsersRpcHandler::class));
}
public function registerRedirectorRoutes(RoutingContext $routingCtx): void {

View file

@ -1,196 +0,0 @@
<?php
namespace Misuzu\OAuth2;
use RuntimeException;
use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcAction};
final class OAuth2RpcHandler implements RpcHandler {
use RpcHandlerCommon;
public function __construct(
private OAuth2Context $oauth2Ctx
) {}
/**
* @return array{
* method: 'basic',
* error?: 'app'|'secret',
* type?: 'confapp'|'pubapp',
* app?: string,
* scope?: string[]
* }
*/
#[RpcAction('hanyuu:oauth2:attemptAppAuth')]
public function procAttemptAppAuth(string $remoteAddr, string $clientId, string $clientSecret = ''): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(clientId: $clientId, deleted: false);
} catch(RuntimeException $ex) {
return ['method' => 'basic', 'error' => 'app'];
}
$authed = false;
if($clientSecret !== '') {
// TODO: rate limiting
if(!$appInfo->verifyClientSecret($clientSecret))
return ['method' => 'basic', 'error' => 'secret'];
$authed = true;
}
return [
'method' => 'basic',
'type' => $authed ? 'confapp' : 'pubapp',
'app' => $appInfo->id,
'scope' => ['oauth2'],
];
}
/**
* @return array{
* method: 'bearer',
* error?: 'token'|'expired',
* type?: 'app'|'user',
* app?: string,
* user?: string,
* scope?: string[],
* expires?: int,
* }
*/
#[RpcAction('hanyuu:oauth2:attemptBearerAuth')]
public function procAttemptBearerAuth(string $remoteAddr, string $token): array {
try {
$tokenInfo = $this->oauth2Ctx->tokens->getAccessInfo($token, OAuth2AccessInfoGetField::Token);
} catch(RuntimeException $ex) {
return ['method' => 'bearer', 'error' => 'token'];
}
if($tokenInfo->expired)
return ['method' => 'bearer', 'error' => 'expired'];
return [
'method' => 'bearer',
'type' => empty($tokenInfo->userId) ? 'app' : 'user',
'app' => $tokenInfo->appId,
'user' => $tokenInfo->userId ?? '0',
'scope' => $tokenInfo->scopes,
'expires' => $tokenInfo->expiresTime,
];
}
/**
* @return array{
* device_code: string,
* user_code: string,
* verification_uri: string,
* verification_uri_complete: string,
* expires_in?: int,
* interval?: int
* }|array{ error: string, error_description: string }
*/
#[RpcAction('hanyuu:oauth2:createAuthoriseRequest')]
public function procCreateAuthoriseRequest(string $appId, ?string $scope = null): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
} catch(RuntimeException $ex) {
return [
'error' => 'invalid_client',
'error_description' => 'No application has been registered with this client ID.',
];
}
return $this->oauth2Ctx->createDeviceAuthorisationRequest($appInfo, $scope);
}
/**
* @return array{
* access_token: string,
* token_type: 'Bearer',
* expires_in?: int,
* scope?: string,
* refresh_token?: string,
* }|array{ error: string, error_description: string }
*/
#[RpcAction('hanyuu:oauth2:createBearerToken:authorisationCode')]
public function procCreateBearerTokenAuthzCode(string $appId, bool $isAuthed, string $code, string $codeVerifier): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
} catch(RuntimeException $ex) {
return [
'error' => 'invalid_client',
'error_description' => 'No application has been registered with this client ID.',
];
}
return $this->oauth2Ctx->redeemAuthorisationCode($appInfo, $isAuthed, $code, $codeVerifier);
}
/**
* @return array{
* access_token: string,
* token_type: 'Bearer',
* expires_in?: int,
* scope?: string,
* refresh_token?: string,
* }|array{ error: string, error_description: string }
*/
#[RpcAction('hanyuu:oauth2:createBearerToken:refreshToken')]
public function procCreateBearerTokenRefreshToken(string $appId, bool $isAuthed, string $refreshToken, ?string $scope = null): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
} catch(RuntimeException $ex) {
return [
'error' => 'invalid_client',
'error_description' => 'No application has been registered with this client ID.',
];
}
return $this->oauth2Ctx->redeemRefreshToken($appInfo, $isAuthed, $refreshToken, $scope);
}
/**
* @return array{
* access_token: string,
* token_type: 'Bearer',
* expires_in?: int,
* scope?: string,
* refresh_token?: string,
* }|array{ error: string, error_description: string }
*/
#[RpcAction('hanyuu:oauth2:createBearerToken:clientCredentials')]
public function procCreateBearerTokenClientCreds(string $appId, bool $isAuthed, ?string $scope = null): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
} catch(RuntimeException $ex) {
return [
'error' => 'invalid_client',
'error_description' => 'No application has been registered with this client ID.',
];
}
return $this->oauth2Ctx->redeemClientCredentials($appInfo, $isAuthed, $scope);
}
/**
* @return array{
* access_token: string,
* token_type: 'Bearer',
* expires_in?: int,
* scope?: string,
* refresh_token?: string,
* }|array{ error: string, error_description: string }
*/
#[RpcAction('hanyuu:oauth2:createBearerToken:deviceCode')]
public function procCreateBearerTokenDeviceCode(string $appId, bool $isAuthed, string $deviceCode): array {
try {
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
} catch(RuntimeException $ex) {
return [
'error' => 'invalid_client',
'error_description' => 'No application has been registered with this client ID.',
];
}
return $this->oauth2Ctx->redeemDeviceCode($appInfo, $isAuthed, $deviceCode);
}
}

View file

@ -1,120 +0,0 @@
<?php
namespace Misuzu\Users;
use RuntimeException;
use Misuzu\SiteInfo;
use Misuzu\Users\Assets\UserAvatarAsset;
use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcQuery};
use Index\XArray;
use Index\Colour\{Colour,ColourRgb};
use Index\Urls\UrlRegistry;
final class UsersRpcHandler implements RpcHandler {
use RpcHandlerCommon;
public function __construct(
private SiteInfo $siteInfo,
private UrlRegistry $urls,
private UsersContext $usersCtx
) {}
/**
* @return array{error: string}|array{
* id: string,
* name: string,
* email?: string,
* colour_raw: int,
* colour_css: string,
* rank: int,
* country_code: string,
* roles?: string[],
* is_super?: bool,
* title?: string,
* created_at: string,
* last_active_at?: string,
* profile_url: string,
* avatar_url: string,
* avatar_urls?: array{res: int, url: string}[],
* is_deleted?: bool
* }
*/
#[RpcQuery('misuzu:users:getUser')]
public function queryGetUser(string $userId, bool $includeEMailAddress = false): array {
try {
$userInfo = $this->usersCtx->getUserInfo($userId, UsersData::GET_USER_ID);
} catch(RuntimeException) {
return ['error' => 'notfound'];
}
// TODO: there should be some kinda privacy controls for users
$colour = $this->usersCtx->getUserColour($userInfo);
if($colour->inherits) {
$colourRaw = null;
$colourCSS = (string)$colour;
} else {
$colourRaw = Colour::toRawRgb($colour);
$colourCSS = (string)ColourRgb::convert($colour);
}
$baseUrl = $this->siteInfo->url;
$output = [];
$output['id'] = $userInfo->id;
$output['name'] = $userInfo->name;
if($includeEMailAddress)
$output['email'] = $userInfo->emailAddress;
$output['colour_raw'] = $colourRaw;
$output['colour_css'] = $colourCSS;
$output['country_code'] = $userInfo->countryCode;
if($this->usersCtx->hasActiveBan($userInfo)) {
$output['rank'] = 0;
$output['roles'] = ['x-banned'];
} else {
$roles = XArray::select(
$this->usersCtx->roles->getRoles(userInfo: $userInfo, hasString: true, orderByRank: true),
fn($roleInfo) => $roleInfo->string,
);
$output['rank'] = $this->usersCtx->getUserRank($userInfo);
if(!empty($roles))
$output['roles'] = $roles;
if($userInfo->super)
$output['is_super'] = true;
}
if(!empty($userInfo->title))
$output['title'] = $userInfo->title;
$output['created_at'] = $userInfo->createdAt->toIso8601ZuluString();
if($userInfo->lastActiveTime !== null)
$output['last_active_at'] = $userInfo->lastActiveAt->toIso8601ZuluString();
$output['profile_url'] = $baseUrl . $this->urls->format('user-profile', ['user' => $userInfo->id]);
$output['avatar_url'] = $baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->id]);
/* Remove the following later */
$avatars = [];
$formatAvatarUrl = fn($res = 0) => (
$baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => $res])
);
$avatars[] = ['res' => 0, 'url' => $formatAvatarUrl()];
foreach(UserAvatarAsset::DIMENSIONS as $res)
$avatars[] = ['res' => $res, 'url' => $formatAvatarUrl($res)];
$avatars = array_reverse($avatars);
$output['avatar_urls'] = $avatars;
/* / */
if($userInfo->deleted)
$output['is_deleted'] = true;
return $output;
}
}