Add demo mode restrictions to user operations.
Added functionalities to restrict certain user operations like update, password change, and deletion in demo mode. This is done to prevent demo users from modifying crucial data. Helper methods are created for standard re-usable checks. Also, Blade directive is added for frontend UI demo checks.
This commit is contained in:
parent
ebc2dfd944
commit
b5db913c24
@ -4,6 +4,10 @@ APP_KEY=
|
|||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_DEMO_MODE=false
|
||||||
|
APP_DEMO_EMAIL=
|
||||||
|
APP_DEMO_PASSWORD=
|
||||||
|
|
||||||
APP_DEFAULT_USER_TIMEZONE=UTC
|
APP_DEFAULT_USER_TIMEZONE=UTC
|
||||||
# Valid languages: ru | en
|
# Valid languages: ru | en
|
||||||
APP_DEFAULT_LOCALE=ru
|
APP_DEFAULT_LOCALE=ru
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
@ -21,4 +22,18 @@ final readonly class Helpers
|
|||||||
public static function getUserTimeZone() {
|
public static function getUserTimeZone() {
|
||||||
return auth()->user()?->timezone ?? config('app.user_timezone');
|
return auth()->user()?->timezone ?? config('app.user_timezone');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isDemoMode(): bool
|
||||||
|
{
|
||||||
|
return config('app.demo_mode', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isDemoModeAndUserDenyUpdate(User $user): bool
|
||||||
|
{
|
||||||
|
if (self::isDemoMode() !== true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user->email === config('app.demo_email');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use App\Captcha\Images\Head;
|
|||||||
use App\Captcha\Images\ImageManager;
|
use App\Captcha\Images\ImageManager;
|
||||||
use App\Captcha\Images\Lines;
|
use App\Captcha\Images\Lines;
|
||||||
use App\Contracts\CryptographyContract;
|
use App\Contracts\CryptographyContract;
|
||||||
|
use App\Helpers\Helpers;
|
||||||
use App\Services\Api\V1\CaptchaGenerateService;
|
use App\Services\Api\V1\CaptchaGenerateService;
|
||||||
use App\Services\CaptchaToken\CaptchaTokenHandler;
|
use App\Services\CaptchaToken\CaptchaTokenHandler;
|
||||||
use App\Services\CryptographyString;
|
use App\Services\CryptographyString;
|
||||||
@ -20,6 +21,7 @@ use App\Services\Search\CreateSearchInstanceCommand;
|
|||||||
use App\Services\Search\Search;
|
use App\Services\Search\Search;
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
use Illuminate\Contracts\Foundation\Application;
|
||||||
use Illuminate\Pagination\Paginator;
|
use Illuminate\Pagination\Paginator;
|
||||||
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
|
||||||
@ -64,6 +66,10 @@ final class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
Blade::if('demo', function () {
|
||||||
|
return Helpers::isDemoMode();
|
||||||
|
});
|
||||||
|
|
||||||
Password::defaults(function () {
|
Password::defaults(function () {
|
||||||
$rule = Password::min(8);
|
$rule = Password::min(8);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Services\Private;
|
|||||||
use App\Dto\Request\Private\Profile\Update;
|
use App\Dto\Request\Private\Profile\Update;
|
||||||
use App\Dto\Request\Private\Profile\UpdateSettings;
|
use App\Dto\Request\Private\Profile\UpdateSettings;
|
||||||
use App\Dto\Request\Private\User\UpdatePassword;
|
use App\Dto\Request\Private\User\UpdatePassword;
|
||||||
|
use App\Helpers\Helpers;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\ServiceResults\ServiceResultError;
|
use App\ServiceResults\ServiceResultError;
|
||||||
use App\ServiceResults\ServiceResultSuccess;
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
@ -19,6 +20,10 @@ final class ProfileService extends Service
|
|||||||
|
|
||||||
public function update(Update $update, User $user): ServiceResultError | ServiceResultSuccess
|
public function update(Update $update, User $user): ServiceResultError | ServiceResultSuccess
|
||||||
{
|
{
|
||||||
|
if (Helpers::isDemoModeAndUserDenyUpdate($user)) {
|
||||||
|
return $this->errValidate(__('Demo Mode'));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$data = [
|
$data = [
|
||||||
'name' => $update->getName()
|
'name' => $update->getName()
|
||||||
@ -33,6 +38,10 @@ final class ProfileService extends Service
|
|||||||
|
|
||||||
public function updatePassword(UpdatePassword $update, User $user): ServiceResultError | ServiceResultSuccess
|
public function updatePassword(UpdatePassword $update, User $user): ServiceResultError | ServiceResultSuccess
|
||||||
{
|
{
|
||||||
|
if (Helpers::isDemoModeAndUserDenyUpdate($user)) {
|
||||||
|
return $this->errValidate(__('Demo Mode'));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->userCommandHandler->handleUpdatePassword($user, $update->getPassword());
|
$this->userCommandHandler->handleUpdatePassword($user, $update->getPassword());
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Services\Private;
|
|||||||
use App\Dto\Builder\User as UserBuilderDto;
|
use App\Dto\Builder\User as UserBuilderDto;
|
||||||
use App\Dto\Request\Private\User\StoreUpdate;
|
use App\Dto\Request\Private\User\StoreUpdate;
|
||||||
use App\Dto\Request\Private\User\UpdatePassword;
|
use App\Dto\Request\Private\User\UpdatePassword;
|
||||||
|
use App\Helpers\Helpers;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Dto\QuerySettingsDto;
|
use App\Dto\QuerySettingsDto;
|
||||||
use App\Repositories\RoleRepository;
|
use App\Repositories\RoleRepository;
|
||||||
@ -120,6 +121,10 @@ final class UserService extends Service
|
|||||||
return $this->errFobidden(__('Access is denied'));
|
return $this->errFobidden(__('Access is denied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
|
||||||
|
return $this->errValidate(__('Demo Mode'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->userRepository->isExistsEmail($data->getEmail(), $modelUser->id)) {
|
if ($this->userRepository->isExistsEmail($data->getEmail(), $modelUser->id)) {
|
||||||
return $this->errValidate(
|
return $this->errValidate(
|
||||||
__('validation.unique', ['attribute' => __('validation.attributes.email')]),
|
__('validation.unique', ['attribute' => __('validation.attributes.email')]),
|
||||||
@ -156,6 +161,10 @@ final class UserService extends Service
|
|||||||
return $this->errFobidden(__('Access is denied'));
|
return $this->errFobidden(__('Access is denied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
|
||||||
|
return $this->errValidate(__('Demo Mode'));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->userCommandHandler->handleUpdatePassword($modelUser, $data->getPassword());
|
$this->userCommandHandler->handleUpdatePassword($modelUser, $data->getPassword());
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@ -178,6 +187,10 @@ final class UserService extends Service
|
|||||||
return $this->errFobidden(__('Access is denied'));
|
return $this->errFobidden(__('Access is denied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
|
||||||
|
return $this->errValidate(__('Demo Mode'));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($modelUser) {
|
DB::transaction(function () use ($modelUser) {
|
||||||
$this->userCommandHandler->handleDestroy($modelUser);
|
$this->userCommandHandler->handleDestroy($modelUser);
|
||||||
|
@ -30,6 +30,18 @@ return [
|
|||||||
|
|
||||||
'env' => env('APP_ENV', 'production'),
|
'env' => env('APP_ENV', 'production'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Demo Mode
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Enables or disables Demo mode.
|
||||||
|
| When Demo mode is enabled, it disables the ability to change user.
|
||||||
|
*/
|
||||||
|
'demo_mode' => (bool) env('APP_DEMO_MODE', false),
|
||||||
|
'demo_email' => env('APP_DEMO_EMAIL', false),
|
||||||
|
'demo_password' => env('APP_DEMO_PASSWORD', false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Application Debug Mode
|
| Application Debug Mode
|
||||||
|
@ -72,5 +72,6 @@
|
|||||||
"Captcha User Agent does not match": "The CAPTCHA verification state has been rejected because the User Agent does not match. Please check and ensure that you are using the same User Agent.",
|
"Captcha User Agent does not match": "The CAPTCHA verification state has been rejected because the User Agent does not match. Please check and ensure that you are using the same User Agent.",
|
||||||
"The time for captcha verification has passed": "The time for captcha verification has passed.",
|
"The time for captcha verification has passed": "The time for captcha verification has passed.",
|
||||||
"Captcha does not pass verification": "Captcha does not pass verification.",
|
"Captcha does not pass verification": "Captcha does not pass verification.",
|
||||||
"Add code to the site": "Add code to the site"
|
"Add code to the site": "Add code to the site",
|
||||||
|
"Demo Mode": "!!! Demo Mode !!!"
|
||||||
}
|
}
|
||||||
|
@ -72,5 +72,6 @@
|
|||||||
"Captcha User Agent does not match": "Отклонено состояние проверки CAPTCHA, так как User Agent не совпадает. Пожалуйста, проверьте и убедитесь, что вы используете тот же User Agent.",
|
"Captcha User Agent does not match": "Отклонено состояние проверки CAPTCHA, так как User Agent не совпадает. Пожалуйста, проверьте и убедитесь, что вы используете тот же User Agent.",
|
||||||
"The time for captcha verification has passed": "Время верификации капчи прошло.",
|
"The time for captcha verification has passed": "Время верификации капчи прошло.",
|
||||||
"Captcha does not pass verification": "Капча не проходит проверку.",
|
"Captcha does not pass verification": "Капча не проходит проверку.",
|
||||||
"Add code to the site": "Добавьте код на сайт"
|
"Add code to the site": "Добавьте код на сайт",
|
||||||
|
"Demo Mode": "!!! Включён демо режим !!!"
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="content min-vh-100 position-relative pb-7 pb-lg-5">
|
<main class="content min-vh-100 position-relative pb-7 pb-lg-5">
|
||||||
|
@demo <p style="border: 1px solid #ff1810; color: #ff1810; font-weight: bold; font-size: 18px; padding: 7px; text-align: center;">{{ __('Demo Mode') }}</p> @endif
|
||||||
<nav class="navbar navbar-top navbar-expand navbar-dashboard navbar-dark ps-0 pe-2 pb-2 pb-lg-3">
|
<nav class="navbar navbar-top navbar-expand navbar-dashboard navbar-dark ps-0 pe-2 pb-2 pb-lg-3">
|
||||||
<div class="container-fluid px-0">
|
<div class="container-fluid px-0">
|
||||||
<div class="d-flex justify-content-between w-100" id="navbarSupportedContent">
|
<div class="d-flex justify-content-between w-100" id="navbarSupportedContent">
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<div class="row justify-content-center form-bg-image" data-background-lg="{{ Vite::asset('resources/volt/images/illustrations/signin.svg') }}">
|
<div class="row justify-content-center form-bg-image" data-background-lg="{{ Vite::asset('resources/volt/images/illustrations/signin.svg') }}">
|
||||||
<div class="col-12 d-flex align-items-center justify-content-center">
|
<div class="col-12 d-flex align-items-center justify-content-center">
|
||||||
<div class="bg-white shadow border-0 rounded border-light p-4 p-lg-5 w-100 fmxw-500">
|
<div class="bg-white shadow border-0 rounded border-light p-4 p-lg-5 w-100 fmxw-500">
|
||||||
|
@demo <p style="border: 1px solid #ff1810; color: #ff1810; font-weight: bold; font-size: 18px; padding: 7px; text-align: center;">{{ __('Demo Mode') }}</p> @endif
|
||||||
<div class="text-center text-md-center mb-4 mt-md-0">
|
<div class="text-center text-md-center mb-4 mt-md-0">
|
||||||
<h1 class="mb-0 h3">{{ __('Sign in to our platform') }}</h1>
|
<h1 class="mb-0 h3">{{ __('Sign in to our platform') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -24,7 +25,7 @@
|
|||||||
<span class="input-group-text" id="basic-addon1">
|
<span class="input-group-text" id="basic-addon1">
|
||||||
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path></svg>
|
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path></svg>
|
||||||
</span>
|
</span>
|
||||||
<input type="email" name="email" class="form-control" placeholder="example@company.com" id="email" autofocus required>
|
<input type="email" name="email" class="form-control" placeholder="example@company.com" @demo value="{{ config('app.demo_email', null) }}" @endif id="email" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End of Form -->
|
<!-- End of Form -->
|
||||||
@ -36,7 +37,7 @@
|
|||||||
<span class="input-group-text" id="basic-addon2">
|
<span class="input-group-text" id="basic-addon2">
|
||||||
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
|
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
|
||||||
</span>
|
</span>
|
||||||
<input type="password" name="password" placeholder="Password" class="form-control" id="password" required>
|
<input type="password" name="password" placeholder="Password" @demo value="{{ config('app.demo_password', null) }}" @endif class="form-control" id="password" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End of Form -->
|
<!-- End of Form -->
|
||||||
|
Loading…
Reference in New Issue
Block a user