diff --git a/app/Config/Events.php b/app/Config/Events.php index 07332c3d9ef5e10c109c3274bbc286156021f6d5..6d7f32bbf041449127d4ff5a4f89a4f07966febb 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -28,7 +28,6 @@ use CodeIgniter\Exceptions\FrameworkException; */ Events::on('pre_system', static function () { - // @phpstan-ignore-next-line if (ENVIRONMENT !== 'testing') { if (ini_get('zlib.output_compression')) { throw FrameworkException::forEnabledZlibOutputCompression(); @@ -46,8 +45,6 @@ Events::on('pre_system', static function () { * Debug Toolbar Listeners. * -------------------------------------------------------------------- * If you delete, they will no longer be collected. - * - * @phpstan-ignore-next-line */ if (CI_DEBUG && ! is_cli()) { Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); diff --git a/modules/Auth/Config/Auth.php b/modules/Auth/Config/Auth.php index e9017374296b69c0594e20a687b217111ea63b9f..24ef0c89a9b29df70fedace4c3581e6cc62a9395 100644 --- a/modules/Auth/Config/Auth.php +++ b/modules/Auth/Config/Auth.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Modules\Auth\Config; use CodeIgniter\Shield\Authentication\Actions\ActionInterface; +use CodeIgniter\Shield\Authentication\Actions\Email2FA; use CodeIgniter\Shield\Config\Auth as ShieldAuth; use Modules\Auth\Models\UserModel; @@ -75,6 +76,14 @@ class Auth extends ShieldAuth */ public bool $allowRegistration = true; + /** + * -------------------------------------------------------------------- + * Allow Two-Factor Authentication + * -------------------------------------------------------------------- + * Determines whether email 2FA is enabled. + */ + public bool $enable2FA = false; + /** * -------------------------------------------------------------------- * Welcome Link Lifetime @@ -108,6 +117,8 @@ class Auth extends ShieldAuth public function __construct() { + parent::__construct(); + $adminGateway = config('Admin') ->gateway; @@ -116,6 +127,12 @@ class Auth extends ShieldAuth 'login' => $adminGateway, 'logout' => $adminGateway, ]; + + // FIXME: enable2FA config can only be updated in the .env + // Using the settings service to have it set in the db causes infinite loop. + if ($this->enable2FA) { + $this->actions['login'] = Email2FA::class; + } } /** diff --git a/modules/Auth/Language/en/Auth.php b/modules/Auth/Language/en/Auth.php index 6928cf9b10fa8c3fd3ba766c1063fab806edcf4a..09e3cd6b0bb7426a23339a416528b828915357fc 100644 --- a/modules/Auth/Language/en/Auth.php +++ b/modules/Auth/Language/en/Auth.php @@ -80,6 +80,10 @@ return [ 'episodes.manage-publications' => 'Can publish/unpublish episodes and posts of podcast #{id}.', 'episodes.manage-comments' => 'Can create/remove episode comments of podcast #{id}.', ], + + // missing keys + 'code' => 'Your 6-digit code', + 'notEnoughPrivilege' => 'You do not have sufficient permissions to access that page.', 'set_password' => 'Set your password', diff --git a/phpstan.neon b/phpstan.neon index 8e79ac0600b4c0a7aafd10195b4de8b89f517e3d..4c46c3faafac82cea6b46b9bb22e609d684d426f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -20,6 +20,11 @@ parameters: - app/Views/* - modules/*/Views/* - themes/* + dynamicConstantNames: + - APP_NAMESPACE + - CI_DEBUG + - ENVIRONMENT + - SODIUM_LIBRARY_VERSION ignoreErrors: - '#Cannot access property [\$a-z_]+ on ((array\|)?object)#' - '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#' diff --git a/public/index.php b/public/index.php index cc758a8b11a8470e3ac42702414b51684702f020..b80d9fbf47ee5188f0c7f6ca00770535a20a529d 100644 --- a/public/index.php +++ b/public/index.php @@ -6,6 +6,18 @@ use CodeIgniter\Config\DotEnv; use Config\Paths; use Config\Services; +// Check PHP version. +$minPhpVersion = '8.0'; // If you update this, don't forget to update `spark`. +if (version_compare(PHP_VERSION, $minPhpVersion, '<')) { + $message = sprintf( + 'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s', + $minPhpVersion, + PHP_VERSION + ); + + exit($message); +} + // Path to the front controller (this file) define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR); diff --git a/public/media/site/index.html b/public/media/site/index.html deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/rector.php b/rector.php index 881a5ebde1fd0ba5ac96b561657ad5dc8a3a56cf..96dad228bcd29d6766f4af7f3ab9a4577ae4ba2f 100644 --- a/rector.php +++ b/rector.php @@ -10,6 +10,7 @@ use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector; use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector; use Rector\Config\RectorConfig; use Rector\Core\ValueObject\PhpVersion; +use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector; use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector; use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; @@ -61,6 +62,7 @@ return static function (RectorConfig $rectorConfig): void { UnSpreadOperatorRector::class, ExplicitMethodCallOverMagicGetSetRector::class, RemoveExtraParametersRector::class, + UnwrapFutureCompatibleIfPhpVersionRector::class, // skip rule in specific directory StringClassNameToClassConstantRector::class => [ diff --git a/spark b/spark index 225422aace746f0fbf24260668959ed9869b6508..306cf95732be2a5859e9ec04e38f32a40085ded4 100644 --- a/spark +++ b/spark @@ -26,6 +26,18 @@ if (strpos(PHP_SAPI, 'cgi') === 0) { exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n"); } +// Check PHP version. +$minPhpVersion = '8.0'; // If you update this, don't forget to update `public/index.php`. +if (version_compare(PHP_VERSION, $minPhpVersion, '<')) { + $message = sprintf( + 'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s', + $minPhpVersion, + PHP_VERSION + ); + + exit($message); +} + // We want errors to be shown when using it from the CLI. error_reporting(-1); ini_set('display_errors', '1'); diff --git a/themes/cp_auth/email_2fa_show.php b/themes/cp_auth/email_2fa_show.php index ddedc530642f1e57f7483e5ee6faf136cafe3a38..9e70a42a4547ef5c63757b2300bad9b0a1b3abed 100644 --- a/themes/cp_auth/email_2fa_show.php +++ b/themes/cp_auth/email_2fa_show.php @@ -1,38 +1,26 @@ +<?= helper('form') ?> <?= $this->extend(config('Auth')->views['layout']) ?> <?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?> <?= $this->section('content') ?> -<div class="container p-5 d-flex justify-content-center"> - <div class="shadow-sm card col-12 col-md-5"> - <div class="card-body"> - <h5 class="mb-5 card-title"><?= lang('Auth.email2FATitle') ?></h5> +<form action="<?= url_to('auth-action-handle') ?>" method="POST" class="flex flex-col w-full gap-y-4"> + <?= csrf_field() ?> - <p><?= lang('Auth.confirmEmailAddress') ?></p> + <Forms.Field + name="email" + label="<?= lang('Auth.email') ?>" + helper="<?= lang('Auth.confirmEmailAddress') ?>" + required="true" + type="email" + inputmode="email" + autocomplete="email" + value="<?= $user->email ?>" + /> - <?php if (session('error')) : ?> - <div class="alert alert-danger"><?= session('error') ?></div> - <?php endif ?> + <Button variant="primary" type="submit" class="self-end"><?= lang('Auth.send') ?></Button> +</form> - <form action="<?= url_to('auth-action-handle') ?>" method="post"> - <?= csrf_field() ?> - - <!-- Email --> - <div class="mb-2"> - <input type="email" class="form-control" name="email" - inputmode="email" autocomplete="email" placeholder="<?= lang('Auth.email') ?>" - <?php /** @var \CodeIgniter\Shield\Entities\User $user */ ?> - value="<?= old('email', $user->email) ?>" required /> - </div> - - <div class="m-3 mx-auto d-grid col-8"> - <button type="submit" class="btn btn-primary btn-block"><?= lang('Auth.send') ?></button> - </div> - - </form> - </div> - </div> -</div> <?= $this->endSection() ?> diff --git a/themes/cp_auth/email_2fa_verify.php b/themes/cp_auth/email_2fa_verify.php index e9dad7f40cfa36c846df27e75ba9695317c45d7d..18c6715624515c2e4753240e0be7cf9e41a8c493 100644 --- a/themes/cp_auth/email_2fa_verify.php +++ b/themes/cp_auth/email_2fa_verify.php @@ -1,36 +1,26 @@ +<?= helper('form') ?> <?= $this->extend(config('Auth')->views['layout']) ?> -<?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?> +<?= $this->section('title') ?><?= lang('Auth.emailEnterCode') ?> <?= $this->endSection() ?> <?= $this->section('content') ?> -<div class="container p-5 d-flex justify-content-center"> - <div class="shadow-sm card col-12 col-md-5"> - <div class="card-body"> - <h5 class="mb-5 card-title"><?= lang('Auth.emailEnterCode') ?></h5> - - <p><?= lang('Auth.emailConfirmCode') ?></p> - - <?php if (session('error') !== null) : ?> - <div class="alert alert-danger"><?= session('error') ?></div> - <?php endif ?> - - <form action="<?= url_to('auth-action-verify') ?>" method="post"> - <?= csrf_field() ?> - - <!-- Code --> - <div class="mb-2"> - <input type="number" class="form-control" name="token" placeholder="000000" - inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" required /> - </div> - - <div class="m-3 mx-auto d-grid col-8"> - <button type="submit" class="btn btn-primary btn-block"><?= lang('Auth.confirm') ?></button> - </div> - - </form> - </div> - </div> -</div> +<form action="<?= url_to('auth-action-verify') ?>" method="POST" class="flex flex-col w-full gap-y-4"> + <?= csrf_field() ?> + + <Forms.Field + name="token" + label="<?= lang('Auth.code') ?>" + helper="<?= lang('Auth.emailConfirmCode') ?>" + pattern="[0-9]*" + placeholder="000000" + required="true" + type="number" + inputmode="numeric" + autocomplete="one-time-code" + /> + + <Button variant="primary" type="submit" class="self-end"><?= lang('Auth.confirm') ?></Button> +</form> <?= $this->endSection() ?>