Dzianis Kotau
About Me

Мy name is Dzianis Kotau. I'm Solutions Architect and Zend Certified PHP Engineer. I'm PHP evangelist and loved in it.

MoonShine Impersonate

Документация версии 1.x

MoonShine Impersonate предоставляет возможность подмены пользователя в проектах на Laravel с использованием админ-панели MoonShine. Функциональность подмены пользователя позволяет админстратору войти на сайт как реальный пользователь. Это может быть полезно, чтобы посмотреть, как сайт виден обычному пользователю, воспроизвести баги от его имени и т.д.

Содержимое

Установка

MoonShine Impersonate спроектирован для использования в составе админ-панели MoonShine. Поэтому, сперва установите админ-панель, следуя инструкциям с официального сайте MoonShine. Затем установите данный пакет при помощи команды:

composer require jampire/moonshine-impersonate

Настройка

MoonShine Impersonate способен работать из коробки с настройками по умолчанию. Но вы можете опубликовать его настройки в свою директорию config и изменть нужные пареметры (зачастую в файле .env):

php artisan vendor:publish --provider="Jampire\MoonshineImpersonate\ImpersonateServiceProvider" --tag=config

Также можно опубликовать файлы локализации для более тонкой настройки:

php artisan vendor:publish --provider="Jampire\MoonshineImpersonate\ImpersonateServiceProvider" --tag=lang

Использование

Базовое использование

Включение подмены пользователя

Т.к. данный пакет работает с системой авторизации, то применять его можно на модели User. Для этого создайте MoonShine ресурс, использующий модель авторизации User вашего приложения.

Для примера, создадим следующий MoonShine ресурс:

<?php

declare(strict_types=1);

namespace App\MoonShine\Resources;

use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use MoonShine\Fields\Email;
use MoonShine\Fields\Text;
use MoonShine\Resources\ModelResource;
use MoonShine\Decorations\Block;
use MoonShine\Fields\ID;

class UserResource extends ModelResource
{
    protected string $model = User::class;

    protected string $title = 'Users';

    public function fields(): array
    {
        return [
            Block::make([
                ID::make()->sortable(),
                Text::make('Name', 'name'),
                Email::make('E-mail', 'email'),
            ]),
        ];
    }

    public function rules(Model $item): array
    {
        return [];
    }
}

Для добавления действия подмены пользователя пакет MoonShine Impersonate предоставляет свою кнопку действия EnterImpersonationActionButton. Для его добавления воспользуйтесь методом buttons() ресурса UserResource:

<?php

declare(strict_types=1);

namespace App\MoonShine\Resources;

use Jampire\MoonshineImpersonate\UI\ActionButtons\EnterImpersonationActionButton
use MoonShine\Resources\ModelResource;

class UserResource extends ModelResource
{
    // ...

    public function buttons(): array
    {
        return [
            EnterImpersonationActionButton::resolve()->showInDropdown(),
        ];
    }
}

За тонкую настройку этого действия отвечают секция buttons.enter файла конфигурации и секция ui.buttons.enter файлов локализации.

После подключения данного действия ваш ресурс Users может выглядеть следующим образом:

Start Impersonation Button

При нажатии на эту кнопку вы переключитесь на выбранного пользователя:

Impersonation Mode

Отключение подмены пользователя

Пакет MoonShine Impersonate предоставляет несколько способов отключения режима подмены пользователя. Рассмотрим способ размещения кнопки отключения в заголовке шаблона. Разместив кнопку в заголовке, вы всегда будите знать, что находитесь в режиме подмены пользователя, т.к. кнопка будет видна на всех страницах админ-панели (когда режим подмены остановлен, кнопка видна не будет).

Для того, чтобы размещать кастомные элементы в заголовке шаблона, вам нужно опубликовать шаблон и отредактировать его (что обычно и происходит, когда вы настраиваете админ-панель):

php artisan moonshine:publish

Для отключения действия подмены пользователя пакет MoonShine Impersonate предоставляет свой компонент StopImpersonation. Для размещения компонента в заголовке добавьте его в компонет Header опубликонвааного шаблона:

<?php

declare(strict_types=1);

namespace App\MoonShine;

// ...
use MoonShine\Components\Layout\{Header,
    LayoutBlock,
    LayoutBuilder};
use Jampire\MoonshineImpersonate\UI\Components\StopImpersonation;
use MoonShine\Contracts\MoonShineLayoutContract;

final class MoonShineLayout implements MoonShineLayoutContract
{
    public static function build(): LayoutBuilder
    {
        return LayoutBuilder::make([
            // ...
            LayoutBlock::make([
                // ...
                Header::make([
                    // ...
                    StopImpersonation::make(),
                ]),
                // ...
            ])->customAttributes(['class' => 'layout-page']),
        ]);
    }
}

Stop Impersonation Button

Middleware

Для своей работы режим подмены пользователя использует ImpersonateMiddleware класс. По-умолчанию он работает с маршрутами (routes) в группе web. Если вы хотите добавить этот middleware к другим маршрутам, воспольуйтесь псевдонимом impersonate. Например, его можно подключить в контроллере:

class PostController extends Controller
{
    public function __construct()
    {
        $this->middleware('impersonate');
    }
}

Разрешения

По умолчанию, любой администратор может подменять любого пользователя. Если вы желаете более тонкую настройку разрешений (permissions), можно воспользоваться интерфейсами Impersonable и BeImpersonable и реализовать методы canImpersonate() и canBeImpersonated() соответственно.

Например, чтобы контролировать, кто из админстраторов может подменять пользователя, нужно реализовать метод canImpersonate() на моделе администратора MoonshineUser:

<?php

declare(strict_types=1);

namespace App\Models;

use Jampire\MoonshineImpersonate\Services\Contracts\Impersonable;
use MoonShine\Models\MoonshineUser as BaseUser;
use MoonShine\Models\MoonshineUserRole;

class MoonshineUser extends BaseUser implements Impersonable
{
    // ...

    public function canImpersonate(): bool
    {
        return $this->moonshine_user_role_id === MoonshineUserRole::DEFAULT_ROLE_ID;
    }
}

А чтобы контролировать, кого из пользователей можно подменять, нужно реализовать метод canBeImpersonated() на моделе авторизации User:

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as BaseUser;
use Illuminate\Support\Str;
use Jampire\MoonshineImpersonate\Services\Contracts\BeImpersonable;

class User extends BaseUser implements BeImpersonable
{
    // ...

    public function canBeImpersonated(): bool
    {
        return !Str::endsWith($this->email, '@verysecureemail.com');
    }
}

Запись событий (логирование)

Если вы желаете, то можете включить запись событий переключения пользователя в журнал MoonShine (по-умолчанию отключено). Для включение режима записи вам необходимо установить пакет MoonShine Changelog и добавить трейт HasChangeLog к модели авторизации User:

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use MoonShine\ChangeLog\Traits\HasChangeLog;

class User extends Authenticatable
{
    use HasChangeLog;

    // ...
}

При старте режима подмены пользователя в журнале (таблица moonshine_change_logs) появится следующая запись:

id: 7
moonshine_user_id: 1 # пользователь, который запустил режим подмены (админ)
changelogable_type: App\Models\User # модель авторизации пользователя
changelogable_id: 4 # пользователь, которого подменяют
states_before: "impersonation_stopped" # статус приложения до начала подмены
states_after: "impersonation_entered" # статус приложения после начала подмены
created_at: 2023-05-25 23:25:11
updated_at: 2023-05-25 23:25:11

При окончании режима подмены пользователя в журнале (таблица moonshine_change_logs) появится следующая запись:

id: 8
moonshine_user_id: 1 # пользователь, который остановил режим подмены (админ)
changelogable_type: App\Models\User # модель авторизации пользователя
changelogable_id: 4 # пользователь, которого подменяли
states_before: "impersonation_entered" # статус приложения до остановки подмены
states_after: "impersonation_stopped" # статус приложения после остановки подмены
created_at: 2023-05-25 23:25:37
updated_at: 2023-05-25 23:25:37

События

После старта и отмены режима подмены пользователя отправляются события ImpersonationEntered и ImpersonationStopped соответственно. Оба эти события обладают следующими открытыми свойствами:

Расширенное использование

Расширенное использование подразумевает, что вы хорошо владете админ-панелью MoonShine и фреймворком Laravel.

Помимо рассмотренных выше кнопки EnterImpersonationActionButton и компонента StopImpersonation, данный пакет также предоставляет кнопку действия StopImpersonationActionButton, которая действует аналогично компоненту StopImpersonation, но предназначена для страниц и ресурсов. Кнопки и компонент обладают всеми свойствами, присущими кнопкам и компонентам в MoonShine. Например, кнопки можно отрисовать в in-line или dropdown режимах.

Также вы можете самостоятельно реализовать свои кнопки и компоненты, используя админ-панель MoonShine и ресурсы пакета MoonShine Impersonate. Пример реализации кнопки включения режима подмены пользователя:

use Illuminate\Contracts\Auth\Authenticatable;
use Jampire\MoonshineImpersonate\Actions\EnterAction;
use MoonShine\ActionButtons\ActionButton;

ActionButton::make(
    label: __('impersonate::ui.buttons.enter.label'),
    url: static fn (mixed $data): string => route('impersonate.enter', [
        config('impersonate.resource_item_key') => $data->getKey(),
    ]),
)
    ->canSee(
        callback: fn (Authenticatable $item): bool => app(EnterAction::class)->manager->canEnter($item),
    )
    ->icon(config('impersonate.buttons.enter.icon'));

Cписок ресурсов пакета MoonShine Impersonate

Дополнительные ресурсы

Функционал подмены пользователя реализуют некоторые админ-панели. Например: