Basic structure for user tables.

This commit is contained in:
Pachira 2023-01-09 21:44:34 +00:00
parent 5e35ccaf55
commit d9e175699e
21 changed files with 747 additions and 11 deletions

View file

View file

@ -0,0 +1,104 @@
<?php
use Index\Data\IDbConnection;
use Index\Data\Migration\IDbMigration;
final class CreateUsersTables_20230109_213225 implements IDbMigration {
public function migrate(IDbConnection $conn): void {
$conn->execute('
CREATE TABLE hau_users (
user_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
user_name VARCHAR(255) NOT NULL COLLATE \'ascii_general_ci\',
user_country CHAR(2) NOT NULL DEFAULT \'XX\' COLLATE \'ascii_general_ci\',
user_colour INT(10) UNSIGNED NULL DEFAULT NULL,
user_super TINYINT(1) UNSIGNED NOT NULL DEFAULT \'0\',
user_time_zone VARBINARY(255) NOT NULL DEFAULT \'UTC\',
user_created TIMESTAMP NOT NULL DEFAULT current_timestamp(),
user_updated TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
user_deleted TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (user_id) USING BTREE,
UNIQUE INDEX hau_users_name_unique (user_name) USING BTREE,
INDEX hau_users_created_index (user_created) USING BTREE,
INDEX hau_users_deleted_index (user_deleted) USING BTREE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
$conn->execute('
CREATE TABLE hau_users_auth (
user_id INT(10) UNSIGNED NOT NULL,
user_auth_type VARCHAR(32) NOT NULL COLLATE \'ascii_general_ci\',
user_auth_enabled TIMESTAMP NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (user_id, user_auth_type) USING BTREE,
INDEX hau_users_auth_user_foreign (user_id) USING BTREE,
CONSTRAINT hau_users_auth_user_foreign
FOREIGN KEY (user_id)
REFERENCES hau_users (user_id)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
$conn->execute('
CREATE TABLE hau_users_backup (
user_id INT(10) UNSIGNED NOT NULL,
user_backup_code BINARY(8) NOT NULL,
user_backup_created TIMESTAMP NOT NULL DEFAULT current_timestamp(),
user_backup_used TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (user_id, user_backup_code) USING BTREE,
INDEX hau_users_backup_used_index (user_backup_used) USING BTREE,
CONSTRAINT hau_users_backup_user_foreign
FOREIGN KEY (user_id)
REFERENCES hau_users (user_id)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
$conn->execute('
CREATE TABLE hau_users_emails (
user_email_address VARCHAR(255) NOT NULL COLLATE \'ascii_general_ci\',
user_id INT(10) UNSIGNED NOT NULL,
user_email_created TIMESTAMP NOT NULL DEFAULT current_timestamp(),
user_email_verified TIMESTAMP NULL DEFAULT NULL,
user_email_recovery TINYINT(1) UNSIGNED NOT NULL DEFAULT \'0\',
PRIMARY KEY (user_email_address) USING BTREE,
UNIQUE INDEX hau_users_emails_user_foreign (user_id) USING BTREE,
INDEX hau_users_emails_created_index (user_email_created) USING BTREE,
INDEX hau_users_emails_recovery_index (user_email_recovery) USING BTREE,
INDEX hau_users_emails_verified_index (user_email_verified) USING BTREE,
CONSTRAINT hau_users_emails_user_foreign
FOREIGN KEY (user_id)
REFERENCES hau_users (user_id)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
$conn->execute('
CREATE TABLE hau_users_passwords (
user_id INT(10) UNSIGNED NOT NULL,
user_password_hash VARBINARY(255) NOT NULL,
user_password_changed TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
UNIQUE INDEX hau_users_passwords_user_foreign (user_id) USING BTREE,
CONSTRAINT hau_users_passwords_user_foreign
FOREIGN KEY (user_id)
REFERENCES hau_users (user_id)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
$conn->execute('
CREATE TABLE hau_users_totp (
user_id INT(10) UNSIGNED NOT NULL,
user_totp_key BINARY(26) NOT NULL,
user_totp_changed TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
UNIQUE INDEX hau_users_totp_user_foreign (user_id) USING BTREE,
CONSTRAINT hau_users_totp_user_foreign
FOREIGN KEY (user_id)
REFERENCES hau_users (user_id)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
}
}

View file

@ -3,6 +3,8 @@ namespace Hanyuu;
use Index\Autoloader;
use Index\Environment;
use Index\Data\DbTools;
use Hanyuu\Config\IConfig;
use Hanyuu\Config\ArrayConfig;
define('HAU_STARTUP', microtime(true));
@ -44,5 +46,9 @@ set_exception_handler(function(\Throwable $ex) {
die('<h2>Hanyuu is sad.</h2>');
});
$hau = new HanyuuContext(ArrayConfig::open(HAU_DIR_CONFIG . '/config.ini'));
$hau->connectDb();
$cfg = ArrayConfig::open(HAU_DIR_CONFIG . '/config.ini');
$dbc = DbTools::create($cfg->getValue('database:dsn', IConfig::T_STR, 'null'));
$dbc->execute('SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';');
$hau = new HanyuuContext($cfg, $dbc);

View file

@ -2,7 +2,6 @@
namespace Hanyuu;
use Index\Data\IDbConnection;
use Index\Data\DbTools;
use Index\Data\Migration\IDbMigrationRepo;
use Index\Data\Migration\DbMigrationManager;
use Index\Data\Migration\FsDbMigrationRepo;
@ -11,29 +10,30 @@ use Index\Http\HttpRequest;
use Index\Routing\IRouter;
use Hanyuu\Config\IConfig;
use Hanyuu\Templating\TemplateContext;
use Hanyuu\Users\IUsers;
use Hanyuu\Users\Db\DbUsers;
class HanyuuContext {
private const DB_INIT = 'SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';';
private IConfig $config;
private IDbConnection $dbConn;
private IUsers $users;
private ?TemplateContext $tpl = null;
public function __construct(IConfig $config) {
public function __construct(IConfig $config, IDbConnection $dbConn) {
$this->config = $config;
$this->dbConn = $dbConn;
$this->users = new DbUsers($dbConn);
}
public function getSiteName(): string {
return $this->config->getValue('site:name', IConfig::T_STR, 'Hanyuu');
}
public function connectDb(?IDbConnection $dbConn = null): void {
$dbConn ??= DbTools::create($this->config->getValue('database:dsn', IConfig::T_STR, 'null'));
$dbConn->execute(self::DB_INIT);
$this->dbConn = $dbConn;
public function getUsers(): IUsers {
return $this->users;
}
public function getDb(): IDbConnection {
public function getDatabase(): IDbConnection {
return $this->dbConn;
}
@ -106,5 +106,26 @@ class HanyuuContext {
$this->router->get('/', function($response, $request) {
return 503;
});
if(!HAU_DEBUG)
return;
$this->router->get('/test', function() {
$users = $this->getUsers();
$userInfo = $users->getUserInfoByName('flAsH');
$ffa = $users->getUserFirstFactorAuthInfos($userInfo);
$tfa = $users->getUserSecondFactorAuthInfos($userInfo);
$pwdInfo = $users->getUserPasswordInfo($userInfo);
$totpInfo = $users->getUserTOTPInfo($userInfo);
$totpGen = $totpInfo->createGenerator();
$emailInfos = $users->getUserEMailInfos($userInfo);
$backupInfos = $users->getUserBackupInfos($userInfo);
});
}
}

View file

@ -0,0 +1,6 @@
<?php
namespace Hanyuu\OTP;
interface IOTPGenerator {
public function generate(): string;
}

74
src/OTP/TOTPGenerator.php Normal file
View file

@ -0,0 +1,74 @@
<?php
namespace Hanyuu\OTP;
use InvalidArgumentException;
use Index\Serialisation\Serialiser;
class TOTPGenerator implements IOTPGenerator {
private const DIGITS = 6;
private const INTERVAL = 30;
private const HASH_ALGO = 'sha1';
public function __construct(
private string $secretKey,
private int $digits = self::DIGITS,
private int $interval = self::INTERVAL,
private string $hashAlgo = self::HASH_ALGO
) {
if(empty($this->secretKey))
throw new InvalidArgumentException('$secretKey may not be empty');
if($this->digits < 1)
throw new InvalidArgumentException('$digits must be a positive integer');
if($this->interval < 1)
throw new InvalidArgumentException('$interval must be a positive integer');
if(!in_array($this->hashAlgo, hash_hmac_algos(), true))
throw new InvalidArgumentException('$hashAlgo must be a hashing algorithm suitable for hmac');
}
public function getDigits(): int {
return $this->digits;
}
public function getInterval(): int {
return $this->interval;
}
public function getHashAlgo(): string {
return $this->hashAlgo;
}
public function getTimeCode(int $offset = 0, int $timeStamp = -1): int {
if($timeStamp < 0)
$timeStamp = time();
// use -1 and 1 to get the previous and next token for user convenience
if($offset !== 0)
$timeStamp += $this->interval * $offset;
return (int)(($timeStamp * 1000) / ($this->interval * 1000));
}
public function generate(int $offset = 0, int $timeStamp = -1): string {
$timeCode = pack('J', $this->getTimeCode($offset, $timeStamp));
$secretKey = Serialiser::base32()->deserialise($this->secretKey);
$hash = hash_hmac($this->hashAlgo, $timeCode, $secretKey, true);
$offset = ord($hash[strlen($hash) - 1]) & 0x0F;
$bin = (ord($hash[$offset]) & 0x7F) << 24;
$bin |= (ord($hash[$offset + 1]) & 0xFF) << 16;
$bin |= (ord($hash[$offset + 2]) & 0xFF) << 8;
$bin |= ord($hash[$offset + 3]) & 0xFF;
$otp = (string)($bin % pow(10, $this->digits));
return str_pad($otp, $this->digits, '0', STR_PAD_LEFT);
}
public static function generateKey(int $bytes = 16): string {
if($length < 1)
throw new InvalidArgumentException('$bytes must be a positive integer');
return Serialiser::base32()->serialise(random_bytes($bytes));
}
}

View file

@ -3,6 +3,9 @@ namespace Hanyuu\Templating;
use RuntimeException;
// this entire thing needs to be redone and integrated into Index
// take this project as an opportunity to do that
class Template {
public function __construct(
private TemplateContext $context,

View file

@ -0,0 +1,30 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\Data\IDbResult;
use Hanyuu\Users\IUserAuthInfo;
class DbUserAuthInfo implements IUserAuthInfo {
private string $userId;
private string $type;
private DateTime $enabled;
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->type = $result->getString(1);
$this->enabled = DateTime::fromUnixTimeSeconds($result->getInteger(2));
}
public function getUserId(): string {
return $this->userId;
}
public function getType(): string {
return $this->type;
}
public function getEnabledTime(): DateTime {
return $this->enabled;
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\Data\IDbResult;
use Hanyuu\Users\IUserBackupInfo;
class DbUserBackupInfo implements IUserBackupInfo {
private string $userId;
private string $code;
private DateTime $created;
private bool $isUsed;
private DateTime $used;
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->code = $result->getString(1);
$this->created = DateTime::fromUnixTimeSeconds($result->getInteger(2));
$this->isUsed = !$result->isNull(3);
$this->used = DateTime::fromUnixTimeSeconds($result->isNull(3) ? 0 : $result->getInteger(3));
}
public function getUserId(): string {
return $this->userId;
}
public function getCode(): string {
return $this->code;
}
public function getCreatedTime(): DateTime {
return $this->created;
}
public function isUsed(): bool {
return $this->isUsed;
}
public function getUsedTime(): DateTime {
return $this->used;
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\Data\IDbResult;
use Hanyuu\Users\IUserEMailInfo;
class DbUserEMailInfo implements IUserEMailInfo {
private string $userId;
private string $address;
private DateTime $created;
private bool $isVerified;
private DateTime $verified;
private bool $isRecovery;
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->address = $result->getString(1);
$this->created = DateTime::fromUnixTimeSeconds($result->getInteger(2));
$this->isVerified = !$result->isNull(3);
$this->verified = DateTime::fromUnixTimeSeconds($result->isNull(3) ? 0 : $result->getInteger(3));
$this->isRecovery = $result->getInteger(4) !== 0;
}
public function getUserId(): string {
return $this->userId;
}
public function getAddress(): string {
return $this->address;
}
public function getCreatedTime(): DateTime {
return $this->created;
}
public function isVerified(): bool {
return $this->isVerified;
}
public function getVerifiedTime(): DateTime {
return $this->verified;
}
public function isRecovery(): bool {
return $this->isRecovery;
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\TimeZoneInfo;
use Index\Colour\Colour;
use Index\Colour\ColourRGB;
use Index\Data\IDbResult;
use Hanyuu\Users\IUserInfo;
class DbUserInfo implements IUserInfo {
private string $id;
private string $name;
private string $country;
private Colour $colour;
private bool $isSuper;
private TimeZoneInfo $timeZone;
private DateTime $created;
private DateTime $updated;
private bool $isDeleted;
private DateTime $deleted;
public function __construct(IDbResult $result) {
$this->id = $result->getString(0);
$this->name = $result->getString(1);
$this->country = $result->getString(2);
$this->colour = $result->isNull(3) ? Colour::none() : ColourRGB::fromRawRGB($result->getInteger(3));
$this->isSuper = $result->getInteger(4) !== 0;
$this->timeZone = new TimeZoneInfo($result->getString(5));
$this->created = DateTime::fromUnixTimeSeconds($result->getInteger(6));
$this->updated = DateTime::fromUnixTimeSeconds($result->getInteger(7));
$this->isDeleted = !$result->isNull(8);
$this->deleted = DateTime::fromUnixTimeSeconds($result->isNull(8) ? 0 : $result->getInteger(8));
}
public function getId(): string {
return $this->id;
}
public function getName(): string {
return $this->name;
}
public function getCountryCode(): string {
return $this->country;
}
public function getColour(): Colour {
return $this->colour;
}
public function isSuper(): bool {
return $this->isSuper;
}
public function getTimeZone(): TimeZoneInfo {
return $this->timeZone;
}
public function getCreatedTime(): DateTime {
return $this->created;
}
public function getUpdatedTime(): DateTime {
return $this->updated;
}
public function isDeleted(): bool {
return $this->isDeleted;
}
public function getDeletedTime(): DateTime {
return $this->deleted;
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\Data\IDbResult;
use Hanyuu\Users\IUserPasswordInfo;
class DbUserPasswordInfo implements IUserPasswordInfo {
private string $userId;
private string $hash;
private DateTime $changed;
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->hash = $result->getString(1);
$this->changed = DateTime::fromUnixTimeSeconds($result->getInteger(2));
}
public function getUserId(): string {
return $this->userId;
}
public function getHash(): string {
return $this->hash;
}
public function getChangedTime(): DateTime {
return $this->changed;
}
public function verifyPassword(string $password): bool {
return password_verify($password, $this->hash);
}
public function needsRehash(string|int|null $algo, array $options = []): bool {
return password_needs_rehash($this->hash, $algo, $options);
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Hanyuu\Users\Db;
use Index\DateTime;
use Index\Data\IDbResult;
use Hanyuu\OTP\TOTPGenerator;
use Hanyuu\Users\IUserTOTPInfo;
class DbUserTOTPInfo implements IUserTOTPInfo {
private string $userId;
private string $secretKey;
private DateTime $changed;
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->secretKey = $result->getString(1);
$this->changed = DateTime::fromUnixTimeSeconds($result->getInteger(2));
}
public function getUserId(): string {
return $this->userId;
}
public function getSecretKey(): string {
return $this->secretKey;
}
public function getChangedTime(): DateTime {
return $this->changed;
}
public function createGenerator(): TOTPGenerator {
return new TOTPGenerator($this->secretKey);
}
}

170
src/Users/Db/DbUsers.php Normal file
View file

@ -0,0 +1,170 @@
<?php
namespace Hanyuu\Users\Db;
use RuntimeException;
use Index\Data\IDbConnection;
use Index\Data\IDbStatement;
use Index\Data\IDbResult;
use Index\Data\DbType;
use Hanyuu\Users\IUsers;
use Hanyuu\Users\IUserInfo;
use Hanyuu\Users\IUserPasswordInfo;
use Hanyuu\Users\IUserTOTPInfo;
class DbUsers implements IUsers {
private const USERS_TABLE = 'hau_users';
private const AUTH_TABLE = 'hau_users_auth';
private const BACKUP_TABLE = 'hau_users_backup';
private const EMAILS_TABLE = 'hau_users_emails';
private const PASSWORDS_TABLE = 'hau_users_passwords';
private const TOTP_TABLE = 'hau_users_totp';
private const USERS_FIELDS = [
'user_id', 'user_name', 'user_country', 'user_colour', 'user_super', 'user_time_zone',
'UNIX_TIMESTAMP(user_created)', 'UNIX_TIMESTAMP(user_updated)', 'UNIX_TIMESTAMP(user_deleted)',
];
private const AUTH_FIELDS = [
'user_id', 'user_auth_type', 'UNIX_TIMESTAMP(user_auth_enabled)',
];
private const PASSWORDS_FIELDS = [
'user_id', 'user_password_hash', 'UNIX_TIMESTAMP(user_password_changed)',
];
private const TOTP_FIELDS = [
'user_id', 'user_totp_key', 'UNIX_TIMESTAMP(user_totp_changed)',
];
private const EMAILS_FIELDS = [
'user_id', 'user_email_address', 'UNIX_TIMESTAMP(user_email_created)', 'UNIX_TIMESTAMP(user_email_verified)', 'user_email_recovery',
];
private const BACKUP_FIELDS = [
'user_id', 'user_backup_code', 'UNIX_TIMESTAMP(user_backup_created)', 'UNIX_TIMESTAMP(user_backup_used)',
];
private const FIRST_FACTOR_AUTH = ['pwd'];
private const SECOND_FACTOR_AUTH = ['totp'];
public function __construct(
private IDbConnection $conn
) {}
private array $statements = [];
private function getStatement(string $name, callable $query): IDbStatement {
if(array_key_exists($name, $this->statements))
return $this->statements[$name];
return $this->statements[$name] = $this->conn->prepare($query());
}
private function fetchUserInfoSingle(IDbResult $result, string $exceptionText): IUserInfo {
if(!$result->next())
throw new RuntimeException($exceptionText);
return new DbUserInfo($result);
}
public function getUserInfoById(string $userId): IUserInfo {
$stmt = $this->getStatement('get info by id', fn() => ('SELECT ' . implode(',', self::USERS_FIELDS) . ' FROM ' . self::USERS_TABLE . ' WHERE user_id = ?'));
$stmt->reset();
$stmt->addParameter(1, $userId, DbType::STRING);
$stmt->execute();
return $this->fetchUserInfoSingle($stmt->getResult(), 'no user with $userId found');
}
public function getUserInfoByName(string $userName): IUserInfo {
$stmt = $this->getStatement('get info by name', fn() => ('SELECT ' . implode(',', self::USERS_FIELDS) . ' FROM ' . self::USERS_TABLE . ' WHERE user_name = ?'));
$stmt->reset();
$stmt->addParameter(1, $userName, DbType::STRING);
$stmt->execute();
return $this->fetchUserInfoSingle($stmt->getResult(), 'no user with $userName found');
}
private function fetchAuthInfoMultiple(IDbResult $result): array {
$array = [];
while($result->next())
$array[] = new DbUserAuthInfo($result);
return $array;
}
public function getUserFirstFactorAuthInfos(IUserInfo $userInfo): array {
$stmt = $this->getStatement('get auth first', fn() => ('SELECT ' . implode(',', self::AUTH_FIELDS) . ' FROM ' . self::AUTH_TABLE . ' WHERE user_id = ? AND user_auth_type IN ("' . implode('", "', self::FIRST_FACTOR_AUTH) . '")'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
return $this->fetchAuthInfoMultiple($stmt->getResult());
}
public function getUserSecondFactorAuthInfos(IUserInfo $userInfo): array {
$stmt = $this->getStatement('get auth second', fn() => ('SELECT ' . implode(',', self::AUTH_FIELDS) . ' FROM ' . self::AUTH_TABLE . ' WHERE user_id = ? AND user_auth_type IN ("' . implode('", "', self::SECOND_FACTOR_AUTH) . '")'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
return $this->fetchAuthInfoMultiple($stmt->getResult());
}
public function getUserPasswordInfo(IUserInfo $userInfo): IUserPasswordInfo {
$stmt = $this->getStatement('get pwd', fn() => ('SELECT ' . implode(',', self::PASSWORDS_FIELDS) . ' FROM ' . self::PASSWORDS_TABLE . ' WHERE user_id = ?'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
$result = $stmt->getResult();
if(!$result->next())
throw new RuntimeException('no password info for $userInfo found');
return new DbUserPasswordInfo($result);
}
public function getUserTOTPInfo(IUserInfo $userInfo): IUserTOTPInfo {
$stmt = $this->getStatement('get totp', fn() => ('SELECT ' . implode(',', self::TOTP_FIELDS) . ' FROM ' . self::TOTP_TABLE . ' WHERE user_id = ?'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
$result = $stmt->getResult();
if(!$result->next())
throw new RuntimeException('no totp info for $userInfo found');
return new DbUserTOTPInfo($result);
}
public function getUserEMailInfos(IUserInfo $userInfo): array {
$stmt = $this->getStatement('get emails', fn() => ('SELECT ' . implode(',', self::EMAILS_FIELDS) . ' FROM ' . self::EMAILS_TABLE . ' WHERE user_id = ?'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
$result = $stmt->getResult();
$array = [];
while($result->next())
$array[] = new DbUserEMailInfo($result);
return $array;
}
public function getUserBackupInfos(IUserInfo $userInfo): array {
$stmt = $this->getStatement('get backups', fn() => ('SELECT ' . implode(',', self::BACKUP_FIELDS) . ' FROM ' . self::BACKUP_TABLE . ' WHERE user_id = ?'));
$stmt->reset();
$stmt->addParameter(1, $userInfo->getId(), DbType::STRING);
$stmt->execute();
$result = $stmt->getResult();
$array = [];
while($result->next())
$array[] = new DbUserBackupInfo($result);
return $array;
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
interface IUserAuthInfo {
public function getUserId(): string;
public function getType(): string;
public function getEnabledTime(): DateTime;
}

View file

@ -0,0 +1,12 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
interface IUserBackupInfo {
public function getUserId(): string;
public function getCode(): string;
public function getCreatedTime(): DateTime;
public function isUsed(): bool;
public function getUsedTime(): DateTime;
}

View file

@ -0,0 +1,13 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
interface IUserEMailInfo {
public function getUserId(): string;
public function getAddress(): string;
public function getCreatedTime(): DateTime;
public function isVerified(): bool;
public function getVerifiedTime(): DateTime;
public function isRecovery(): bool;
}

19
src/Users/IUserInfo.php Normal file
View file

@ -0,0 +1,19 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
use Index\TimeZoneInfo;
use Index\Colour\Colour;
interface IUserInfo {
public function getId(): string;
public function getName(): string;
public function getCountryCode(): string;
public function getColour(): Colour;
public function isSuper(): bool;
public function getTimeZone(): TimeZoneInfo;
public function getCreatedTime(): DateTime;
public function getUpdatedTime(): DateTime;
public function isDeleted(): bool;
public function getDeletedTime(): DateTime;
}

View file

@ -0,0 +1,12 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
interface IUserPasswordInfo {
public function getUserId(): string;
public function getHash(): string;
public function getChangedTime(): DateTime;
public function verifyPassword(string $password): bool;
public function needsRehash(string|int|null $algo, array $options = []): bool;
}

View file

@ -0,0 +1,12 @@
<?php
namespace Hanyuu\Users;
use Index\DateTime;
use Hanyuu\OTP\TOTPGenerator;
interface IUserTOTPInfo {
public function getUserId(): string;
public function getSecretKey(): string;
public function getChangedTime(): DateTime;
public function createGenerator(): TOTPGenerator;
}

6
src/Users/IUsers.php Normal file
View file

@ -0,0 +1,6 @@
<?php
namespace Hanyuu\Users;
interface IUsers {
//
}