feat(auth): add auth.enable2FA config to enable two-factor authentication
+ update phpstan and rector configs
This commit is contained in:
parent
c1287cbe6c
commit
7213ed290c
|
@ -28,7 +28,6 @@ use CodeIgniter\Exceptions\FrameworkException;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Events::on('pre_system', static function () {
|
Events::on('pre_system', static function () {
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
if (ENVIRONMENT !== 'testing') {
|
if (ENVIRONMENT !== 'testing') {
|
||||||
if (ini_get('zlib.output_compression')) {
|
if (ini_get('zlib.output_compression')) {
|
||||||
throw FrameworkException::forEnabledZlibOutputCompression();
|
throw FrameworkException::forEnabledZlibOutputCompression();
|
||||||
|
@ -46,8 +45,6 @@ Events::on('pre_system', static function () {
|
||||||
* Debug Toolbar Listeners.
|
* Debug Toolbar Listeners.
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* If you delete, they will no longer be collected.
|
* If you delete, they will no longer be collected.
|
||||||
*
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
*/
|
*/
|
||||||
if (CI_DEBUG && ! is_cli()) {
|
if (CI_DEBUG && ! is_cli()) {
|
||||||
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect');
|
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect');
|
||||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Modules\Auth\Config;
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
|
use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
|
||||||
|
use CodeIgniter\Shield\Authentication\Actions\Email2FA;
|
||||||
use CodeIgniter\Shield\Config\Auth as ShieldAuth;
|
use CodeIgniter\Shield\Config\Auth as ShieldAuth;
|
||||||
use Modules\Auth\Models\UserModel;
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
|
@ -75,6 +76,14 @@ class Auth extends ShieldAuth
|
||||||
*/
|
*/
|
||||||
public bool $allowRegistration = true;
|
public bool $allowRegistration = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Allow Two-Factor Authentication
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Determines whether email 2FA is enabled.
|
||||||
|
*/
|
||||||
|
public bool $enable2FA = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* Welcome Link Lifetime
|
* Welcome Link Lifetime
|
||||||
|
@ -108,6 +117,8 @@ class Auth extends ShieldAuth
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
$adminGateway = config('Admin')
|
$adminGateway = config('Admin')
|
||||||
->gateway;
|
->gateway;
|
||||||
|
|
||||||
|
@ -116,6 +127,12 @@ class Auth extends ShieldAuth
|
||||||
'login' => $adminGateway,
|
'login' => $adminGateway,
|
||||||
'logout' => $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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -80,6 +80,10 @@ return [
|
||||||
'episodes.manage-publications' => 'Can publish/unpublish episodes and posts of podcast #{id}.',
|
'episodes.manage-publications' => 'Can publish/unpublish episodes and posts of podcast #{id}.',
|
||||||
'episodes.manage-comments' => 'Can create/remove episode comments 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.',
|
'notEnoughPrivilege' => 'You do not have sufficient permissions to access that page.',
|
||||||
'set_password' => 'Set your password',
|
'set_password' => 'Set your password',
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,11 @@ parameters:
|
||||||
- app/Views/*
|
- app/Views/*
|
||||||
- modules/*/Views/*
|
- modules/*/Views/*
|
||||||
- themes/*
|
- themes/*
|
||||||
|
dynamicConstantNames:
|
||||||
|
- APP_NAMESPACE
|
||||||
|
- CI_DEBUG
|
||||||
|
- ENVIRONMENT
|
||||||
|
- SODIUM_LIBRARY_VERSION
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
|
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
|
||||||
- '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#'
|
- '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#'
|
||||||
|
|
|
@ -6,6 +6,18 @@ use CodeIgniter\Config\DotEnv;
|
||||||
use Config\Paths;
|
use Config\Paths;
|
||||||
use Config\Services;
|
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)
|
// Path to the front controller (this file)
|
||||||
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
|
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
|
||||||
use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector;
|
use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector;
|
||||||
use Rector\Config\RectorConfig;
|
use Rector\Config\RectorConfig;
|
||||||
use Rector\Core\ValueObject\PhpVersion;
|
use Rector\Core\ValueObject\PhpVersion;
|
||||||
|
use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector;
|
||||||
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
|
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
|
||||||
use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
|
use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
|
||||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||||
|
@ -61,6 +62,7 @@ return static function (RectorConfig $rectorConfig): void {
|
||||||
UnSpreadOperatorRector::class,
|
UnSpreadOperatorRector::class,
|
||||||
ExplicitMethodCallOverMagicGetSetRector::class,
|
ExplicitMethodCallOverMagicGetSetRector::class,
|
||||||
RemoveExtraParametersRector::class,
|
RemoveExtraParametersRector::class,
|
||||||
|
UnwrapFutureCompatibleIfPhpVersionRector::class,
|
||||||
|
|
||||||
// skip rule in specific directory
|
// skip rule in specific directory
|
||||||
StringClassNameToClassConstantRector::class => [
|
StringClassNameToClassConstantRector::class => [
|
||||||
|
|
12
spark
12
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");
|
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.
|
// We want errors to be shown when using it from the CLI.
|
||||||
error_reporting(-1);
|
error_reporting(-1);
|
||||||
ini_set('display_errors', '1');
|
ini_set('display_errors', '1');
|
||||||
|
|
|
@ -1,38 +1,26 @@
|
||||||
|
<?= helper('form') ?>
|
||||||
<?= $this->extend(config('Auth')->views['layout']) ?>
|
<?= $this->extend(config('Auth')->views['layout']) ?>
|
||||||
|
|
||||||
<?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?>
|
<?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?>
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
<div class="container p-5 d-flex justify-content-center">
|
<form action="<?= url_to('auth-action-handle') ?>" method="POST" class="flex flex-col w-full gap-y-4">
|
||||||
<div class="shadow-sm card col-12 col-md-5">
|
<?= csrf_field() ?>
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="mb-5 card-title"><?= lang('Auth.email2FATitle') ?></h5>
|
|
||||||
|
|
||||||
<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')) : ?>
|
<Button variant="primary" type="submit" class="self-end"><?= lang('Auth.send') ?></Button>
|
||||||
<div class="alert alert-danger"><?= session('error') ?></div>
|
</form>
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<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() ?>
|
<?= $this->endSection() ?>
|
||||||
|
|
|
@ -1,36 +1,26 @@
|
||||||
|
<?= helper('form') ?>
|
||||||
<?= $this->extend(config('Auth')->views['layout']) ?>
|
<?= $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') ?>
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
<div class="container p-5 d-flex justify-content-center">
|
<form action="<?= url_to('auth-action-verify') ?>" method="POST" class="flex flex-col w-full gap-y-4">
|
||||||
<div class="shadow-sm card col-12 col-md-5">
|
<?= csrf_field() ?>
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="mb-5 card-title"><?= lang('Auth.emailEnterCode') ?></h5>
|
|
||||||
|
|
||||||
<p><?= lang('Auth.emailConfirmCode') ?></p>
|
<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"
|
||||||
|
/>
|
||||||
|
|
||||||
<?php if (session('error') !== null) : ?>
|
<Button variant="primary" type="submit" class="self-end"><?= lang('Auth.confirm') ?></Button>
|
||||||
<div class="alert alert-danger"><?= session('error') ?></div>
|
</form>
|
||||||
<?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>
|
|
||||||
|
|
||||||
<?= $this->endSection() ?>
|
<?= $this->endSection() ?>
|
||||||
|
|
Loading…
Reference in New Issue