Removed local user storage.

This commit is contained in:
flash 2025-01-03 19:45:50 +00:00
parent 25599ddd08
commit 36994255f7
12 changed files with 125 additions and 145 deletions

View file

@ -0,0 +1,18 @@
<?php
use Index\Db\DbConnection;
use Index\Db\Migration\DbMigration;
final class RemoveLocalUsersTable_20250103_190130 implements DbMigration {
public function migrate(DbConnection $conn): void {
// Remove foreign key from prm_uploads
$conn->execute(<<<SQL
ALTER TABLE prm_uploads
DROP INDEX prm_uploads_user_foreign,
ADD INDEX prm_uploads_user_index (user_id) USING BTREE,
DROP FOREIGN KEY prm_uploads_user_foreign
SQL);
// Nuke table
$conn->execute('DROP TABLE prm_users');
}
}

View file

@ -1,30 +1,9 @@
<?php <?php
namespace EEPROM\Auth; namespace EEPROM\Auth;
use EEPROM\Users\UserInfo; interface AuthInfo {
public bool $authed { get; }
class AuthInfo { public ?string $userId { get; }
public private(set) ?UserInfo $userInfo; public bool $restricted { get; }
public int $legacyMultiplier { get; }
public function __construct() {
$this->setInfo();
}
public function setInfo(
?UserInfo $userInfo = null
): void {
$this->userInfo = $userInfo;
}
public function removeInfo(): void {
$this->setInfo();
}
public bool $loggedIn {
get => $this->userInfo !== null;
}
public ?string $userId {
get => $this->userInfo?->id;
}
} }

View file

@ -0,0 +1,26 @@
<?php
namespace EEPROM\Auth;
class AuthInfoWrapper implements AuthInfo {
public AuthInfo $value;
public function __construct(?AuthInfo $authInfo = null) {
$this->value = $authInfo ?? BlankAuthInfo::instance();
}
public bool $authed {
get => $this->value->authed;
}
public ?string $userId {
get => $this->value->userId;
}
public bool $restricted {
get => $this->value->restricted;
}
public int $legacyMultiplier {
get => $this->value->legacyMultiplier;
}
}

View file

@ -1,7 +1,7 @@
<?php <?php
namespace EEPROM\Auth; namespace EEPROM\Auth;
use EEPROM\Users\UsersContext; use EEPROM\Auth\{BlankAuthInfo,FlashiiAuthInfo};
use Flashii\{FlashiiClient,FlashiiUrls}; use Flashii\{FlashiiClient,FlashiiUrls};
use Flashii\Credentials\{BearerCredentials,MisuzuCredentials}; use Flashii\Credentials\{BearerCredentials,MisuzuCredentials};
use Index\Config\Config; use Index\Config\Config;
@ -12,8 +12,7 @@ class AuthRoutes implements RouteHandler {
public function __construct( public function __construct(
private Config $config, private Config $config,
private AuthInfo $authInfo, private AuthInfoWrapper $authInfo
private UsersContext $usersCtx
) {} ) {}
#[HttpMiddleware('/')] #[HttpMiddleware('/')]
@ -39,9 +38,8 @@ class AuthRoutes implements RouteHandler {
$this->config->getString('api', FlashiiUrls::PROD_API_URL) $this->config->getString('api', FlashiiUrls::PROD_API_URL)
)); ));
$authInfo = $flashii->v1()->me(); $userInfo = $flashii->v1()->me();
if($authInfo !== null) $this->authInfo->value = $userInfo === null ? BlankAuthInfo::instance() : new FlashiiAuthInfo($userInfo);
$this->authInfo->setInfo($this->usersCtx->getUser($authInfo->getId()));
} }
} }
} }

View file

@ -0,0 +1,21 @@
<?php
namespace EEPROM\Auth;
class BlankAuthInfo implements AuthInfo {
public private(set) bool $authed = false;
public private(set) ?string $userId = null;
public private(set) bool $restricted = true;
public private(set) int $legacyMultiplier = 0;
private static BlankAuthInfo $instance;
public static function init(): void {
self::$instance = new BlankAuthInfo;
}
public static function instance(): BlankAuthInfo {
return self::$instance;
}
}
BlankAuthInfo::init();

View file

@ -0,0 +1,29 @@
<?php
namespace EEPROM\Auth;
use Flashii\V1\Users\V1User;
class FlashiiAuthInfo implements AuthInfo {
public function __construct(
private V1User $userInfo
) {}
public private(set) bool $authed = true;
public ?string $userId {
get => $this->userInfo->getId();
}
public bool $restricted {
// temporary hack
get => $this->userInfo->hasRole('x-banned');
}
// replace this with an extended version of constrain_size that supports role specific size limits
public int $legacyMultiplier {
get => $this->userInfo->hasRole('admin')
|| $this->userInfo->hasRole('gmod')
|| $this->userInfo->hasRole('dev')
|| $this->userInfo->hasRole('tenshi') ? 2 : 1;
}
}

View file

@ -3,37 +3,32 @@ namespace EEPROM;
use Index\Config\Config; use Index\Config\Config;
use Index\Db\DbConnection; use Index\Db\DbConnection;
use EEPROM\Auth\AuthInfo; use EEPROM\Auth\AuthInfoWrapper;
use RPCii\HmacVerificationProvider; use RPCii\HmacVerificationProvider;
use RPCii\Server\HttpRpcServer; use RPCii\Server\HttpRpcServer;
class EEPROMContext { class EEPROMContext {
public private(set) DatabaseContext $database; public private(set) DatabaseContext $database;
public private(set) SnowflakeGenerator $snowflake; public private(set) SnowflakeGenerator $snowflake;
public private(set) AuthInfoWrapper $authInfo;
private AuthInfo $authInfo;
public private(set) Denylist\DenylistContext $denylistCtx; public private(set) Denylist\DenylistContext $denylistCtx;
public private(set) Pools\PoolsContext $poolsCtx; public private(set) Pools\PoolsContext $poolsCtx;
public private(set) Storage\StorageContext $storageCtx; public private(set) Storage\StorageContext $storageCtx;
public private(set) Uploads\UploadsContext $uploadsCtx; public private(set) Uploads\UploadsContext $uploadsCtx;
private Users\UsersContext $usersCtx;
public function __construct( public function __construct(
private Config $config, private Config $config,
DbConnection $dbConn DbConnection $dbConn
) { ) {
$this->snowflake = new SnowflakeGenerator;
$this->database = new DatabaseContext($dbConn); $this->database = new DatabaseContext($dbConn);
$this->snowflake = new SnowflakeGenerator;
$this->authInfo = new AuthInfo; $this->authInfo = new AuthInfoWrapper;
$this->denylistCtx = new Denylist\DenylistContext($dbConn); $this->denylistCtx = new Denylist\DenylistContext($dbConn);
$this->poolsCtx = new Pools\PoolsContext($dbConn); $this->poolsCtx = new Pools\PoolsContext($dbConn);
$this->storageCtx = new Storage\StorageContext($config->scopeTo('storage'), $dbConn, $this->snowflake); $this->storageCtx = new Storage\StorageContext($config->scopeTo('storage'), $dbConn, $this->snowflake);
$this->uploadsCtx = new Uploads\UploadsContext($config->scopeTo('domain'), $dbConn, $this->snowflake); $this->uploadsCtx = new Uploads\UploadsContext($config->scopeTo('domain'), $dbConn, $this->snowflake);
$this->usersCtx = new Users\UsersContext($dbConn);
} }
public function createRouting(bool $isApiDomain): RoutingContext { public function createRouting(bool $isApiDomain): RoutingContext {
@ -48,8 +43,7 @@ class EEPROMContext {
$routingCtx->register(new Auth\AuthRoutes( $routingCtx->register(new Auth\AuthRoutes(
$this->config->scopeTo('apii'), $this->config->scopeTo('apii'),
$this->authInfo, $this->authInfo
$this->usersCtx
)); ));
$routingCtx->register(new LandingRoutes($this->database)); $routingCtx->register(new LandingRoutes($this->database));

View file

@ -8,7 +8,6 @@ use Index\Db\{DbConnection,DbStatementCache,DbTools};
use EEPROM\SnowflakeGenerator; use EEPROM\SnowflakeGenerator;
use EEPROM\Pools\PoolInfo; use EEPROM\Pools\PoolInfo;
use EEPROM\Storage\StorageRecord; use EEPROM\Storage\StorageRecord;
use EEPROM\Users\UserInfo;
class UploadsData { class UploadsData {
private DbStatementCache $cache; private DbStatementCache $cache;
@ -64,12 +63,12 @@ class UploadsData {
public function getUpload( public function getUpload(
?string $uploadId = null, ?string $uploadId = null,
PoolInfo|string|null $poolInfo = null, PoolInfo|string|null $poolInfo = null,
UserInfo|string|null $userInfo = null, string|null|false $userId = false,
?string $secret = null, ?string $secret = null,
): UploadInfo { ): UploadInfo {
$hasUploadId = $uploadId !== null; $hasUploadId = $uploadId !== null;
$hasPoolInfo = $poolInfo !== null; $hasPoolInfo = $poolInfo !== null;
$hasUserInfo = $userInfo !== null; $hasUserId = $userId !== false;
$hasSecret = $secret !== null; $hasSecret = $secret !== null;
$args = 0; $args = 0;
@ -80,8 +79,8 @@ class UploadsData {
} }
if($hasPoolInfo) if($hasPoolInfo)
$query .= sprintf(' %s pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); $query .= sprintf(' %s pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasUserInfo) if($hasUserId)
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); $query .= sprintf(' %s user_id %s', ++$args > 1 ? 'AND' : 'WHERE', $userId === null ? 'IS NULL' : '= ?');
if($hasSecret) if($hasSecret)
$query .= sprintf(' %s upload_secret = ?', ++$args > 1 ? 'AND' : 'WHERE'); $query .= sprintf(' %s upload_secret = ?', ++$args > 1 ? 'AND' : 'WHERE');
@ -90,8 +89,8 @@ class UploadsData {
$stmt->nextParameter($uploadId); $stmt->nextParameter($uploadId);
if($hasPoolInfo) if($hasPoolInfo)
$stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo); $stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo);
if($hasUserInfo) if($hasUserId && $userId !== null)
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo); $stmt->nextParameter($userId);
if($hasSecret) if($hasSecret)
$stmt->nextParameter($secret); $stmt->nextParameter($secret);
$stmt->execute(); $stmt->execute();
@ -105,7 +104,7 @@ class UploadsData {
public function resolveUploadFromVariant( public function resolveUploadFromVariant(
PoolInfo|string $poolInfo, PoolInfo|string $poolInfo,
UserInfo|string $userInfo, ?string $userId,
StorageRecord|string $fileInfo, StorageRecord|string $fileInfo,
string $variant string $variant
): string { ): string {
@ -120,7 +119,7 @@ class UploadsData {
AND uf.upload_variant = ? AND uf.upload_variant = ?
SQL); SQL);
$stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo); $stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo);
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo); $stmt->nextParameter($userId);
$stmt->nextParameter($fileInfo instanceof StorageRecord ? $fileInfo->id : $fileInfo); $stmt->nextParameter($fileInfo instanceof StorageRecord ? $fileInfo->id : $fileInfo);
$stmt->nextParameter($variant); $stmt->nextParameter($variant);
$stmt->execute(); $stmt->execute();
@ -134,7 +133,7 @@ class UploadsData {
public function createUpload( public function createUpload(
PoolInfo|string $poolInfo, PoolInfo|string $poolInfo,
UserInfo|string $userInfo, ?string $userId,
string $fileName, string $fileName,
string $remoteAddr string $remoteAddr
): UploadInfo { ): UploadInfo {
@ -143,7 +142,7 @@ class UploadsData {
$stmt = $this->cache->get('INSERT INTO prm_uploads (upload_id, pool_id, user_id, upload_secret, upload_name, upload_ip) VALUES (?, ?, ?, ?, ?, INET6_ATON(?))'); $stmt = $this->cache->get('INSERT INTO prm_uploads (upload_id, pool_id, user_id, upload_secret, upload_name, upload_ip) VALUES (?, ?, ?, ?, ?, INET6_ATON(?))');
$stmt->nextParameter($uploadId); $stmt->nextParameter($uploadId);
$stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo); $stmt->nextParameter($poolInfo instanceof PoolInfo ? $poolInfo->id : $poolInfo);
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo); $stmt->nextParameter($userId);
$stmt->nextParameter(XString::random(4)); $stmt->nextParameter(XString::random(4));
$stmt->nextParameter($fileName); $stmt->nextParameter($fileName);
$stmt->nextParameter($remoteAddr); $stmt->nextParameter($remoteAddr);

View file

@ -157,11 +157,10 @@ class UploadsRoutes implements RouteHandler {
if($request->getMethod() === 'OPTIONS') if($request->getMethod() === 'OPTIONS')
return 204; return 204;
if(!$this->authInfo->loggedIn) if(!$this->authInfo->authed)
return 401; return 401;
$userInfo = $this->authInfo->userInfo; if($this->authInfo->restricted)
if($userInfo->restricted)
return 403; return 403;
if(!$request->isFormContent()) if(!$request->isFormContent())
@ -197,7 +196,7 @@ class UploadsRoutes implements RouteHandler {
$maxFileSize = $maxSizeRule->maxSize; $maxFileSize = $maxSizeRule->maxSize;
if($maxSizeRule->allowLegacyMultiplier) if($maxSizeRule->allowLegacyMultiplier)
$maxFileSize *= $userInfo->dataSizeMultiplier; $maxFileSize *= $this->authInfo->legacyMultiplier;
if($file->getSize() !== $fileSize || $fileSize > $maxFileSize) { if($file->getSize() !== $fileSize || $fileSize > $maxFileSize) {
$response->setHeader('Access-Control-Expose-Headers', 'X-EEPROM-Max-Size'); $response->setHeader('Access-Control-Expose-Headers', 'X-EEPROM-Max-Size');
@ -230,7 +229,7 @@ class UploadsRoutes implements RouteHandler {
// TODO: Okay so we're reading from the prm_uploads_files table, then fetching the info from prm_uploads, // TODO: Okay so we're reading from the prm_uploads_files table, then fetching the info from prm_uploads,
// and then fetching the info again from the prm_uploads_files table... this can be done better but this will do for now // and then fetching the info again from the prm_uploads_files table... this can be done better but this will do for now
$uploadInfo = $this->uploadsCtx->uploads->getUpload( $uploadInfo = $this->uploadsCtx->uploads->getUpload(
uploadId: $this->uploadsCtx->uploads->resolveUploadFromVariant($poolInfo, $userInfo, $storageInfo, '') uploadId: $this->uploadsCtx->uploads->resolveUploadFromVariant($poolInfo, $this->authInfo->userId, $storageInfo, '')
); );
$variantInfo = $this->uploadsCtx->uploads->getUploadVariant($uploadInfo, ''); $variantInfo = $this->uploadsCtx->uploads->getUploadVariant($uploadInfo, '');
@ -242,7 +241,7 @@ class UploadsRoutes implements RouteHandler {
} catch(RuntimeException $ex) { } catch(RuntimeException $ex) {
$uploadInfo = $this->uploadsCtx->uploads->createUpload( $uploadInfo = $this->uploadsCtx->uploads->createUpload(
$poolInfo, $poolInfo,
$userInfo, $this->authInfo->userId,
$fileName, $fileName,
$request->getRemoteAddress() $request->getRemoteAddress()
); );
@ -267,7 +266,7 @@ class UploadsRoutes implements RouteHandler {
$response->setHeader('Access-Control-Allow-Headers', 'Authorization'); $response->setHeader('Access-Control-Allow-Headers', 'Authorization');
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, DELETE'); $response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, DELETE');
if(!$this->authInfo->loggedIn) if(!$this->authInfo->authed)
return 401; return 401;
if(strlen($uploadId) === 32) { if(strlen($uploadId) === 32) {
@ -290,8 +289,7 @@ class UploadsRoutes implements RouteHandler {
return 404; return 404;
} }
$userInfo = $this->authInfo->userInfo; if($this->authInfo->restricted || $this->authInfo->userId !== $uploadInfo->userId)
if($userInfo->restricted || $userInfo->id !== $uploadInfo->userId)
return 403; return 403;
$this->uploadsCtx->uploads->deleteUpload($uploadInfo); $this->uploadsCtx->uploads->deleteUpload($uploadInfo);

View file

@ -1,35 +0,0 @@
<?php
namespace EEPROM\Users;
use Carbon\CarbonImmutable;
use Index\Db\DbResult;
class UserInfo {
public function __construct(
public private(set) string $id,
public private(set) int $createdTime,
public private(set) ?int $restrictedTime,
public private(set) int $dataSizeMultiplier,
) {}
public static function fromResult(DbResult $result): UserInfo {
return new UserInfo(
id: $result->getString(0),
createdTime: $result->getInteger(1),
restrictedTime: $result->getIntegerOrNull(2),
dataSizeMultiplier: $result->getInteger(3),
);
}
public CarbonImmutable $createdAt {
get => CarbonImmutable::createFromTimestampUTC($this->createdTime);
}
public bool $restricted {
get => $this->restrictedTime !== null;
}
public ?CarbonImmutable $restrictedAt {
get => $this->restrictedTime === null ? null : CarbonImmutable::createFromTimestampUTC($this->restrictedTime);
}
}

View file

@ -1,17 +0,0 @@
<?php
namespace EEPROM\Users;
use Index\Db\DbConnection;
class UsersContext {
public private(set) UsersData $users;
public function __construct(DbConnection $dbConn) {
$this->users = new UsersData($dbConn);
}
public function getUser(string $userId): UserInfo {
$this->users->ensureUserExists($userId);
return $this->users->getUser($userId);
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace EEPROM\Users;
use Index\Db\{DbConnection,DbStatementCache};
// restrictions and permissions should be checked with misuzu or cached or something
// size multiplier should also just be replaced with an alternate max size value for premioids, maybe based on rank?
class UsersData {
private DbStatementCache $cache;
public function __construct(DbConnection $dbConn) {
$this->cache = new DbStatementCache($dbConn);
}
public function getUser(string $userId): ?UserInfo {
$stmt = $this->cache->get('SELECT user_id, UNIX_TIMESTAMP(user_created), UNIX_TIMESTAMP(user_restricted), user_size_multiplier FROM prm_users WHERE user_id = ?');
$stmt->nextParameter($userId);
$stmt->execute();
$result = $stmt->getResult();
return $result->next() ? UserInfo::fromResult($result) : null;
}
public function ensureUserExists(string $userId): void {
$stmt = $this->cache->get('INSERT IGNORE INTO prm_users (user_id) VALUES (?)');
$stmt->nextParameter($userId);
$stmt->execute();
}
}