Задать вопрос

Как регулировать уровни доступа к модулям Admin Architect — Laravel 5.2?

В Admin Architect сделал админку, но ума не приложу как запретить определенной группе пользователей доступ к определенным модулям.
Обычно для этого используют Middleware, но проблема в том что роуты админки слишком абстрактные:
<?php

$pattern = '[a-z0-9\_\=]+';

Route::group([
    'prefix'    => 'area',
    'namespace' => 'Terranet\Administrator',
    'middleware'=> ['web'],
], function () use ($pattern) {
    /*
    |-------------------------------------------------------
    | Authentication
    |-------------------------------------------------------
    */
    Route::get('login', [
        'as'   => 'scaffold.login',
        'uses' => 'AuthController@getLogin',
    ]);

    Route::post('login', 'AuthController@postLogin');

    Route::get('logout', [
        'as'   => 'scaffold.logout',
        'uses' => 'AuthController@getLogout',
    ]);

    /*
    |-------------------------------------------------------
    | Main Scaffolding routes
    |-------------------------------------------------------
    */
    Route::group(['prefix'    => 'admin',], function () use ($pattern) {
        /*
        |-------------------------------------------------------
        | Custom routes
        |-------------------------------------------------------
        |
        | Controllers that shouldn't be handled by Scaffolding controller
        | goes here.
        |
        */
        //        Route::controllers([
        //            'test' => 'App\Http\Controllers\Admin\TestController'
        //        ]);

        /*
        |-------------------------------------------------------
        | Scaffolding routes
        |-------------------------------------------------------
        */
        // Dashboard
        Route::get('/', [
            'as'   => 'scaffold.dashboard',
            'uses' => 'DashboardController@index',
        ]);

        // Index
        Route::get('{module}', [
            'as'   => 'scaffold.index',
            'uses' => 'Controller@index',
        ])->where('module', $pattern);

        // Create new Item
        Route::get('{module}/create', [
            'as'   => 'scaffold.create',
            'uses' => 'Controller@create',
        ])->where('module', $pattern);
        //и т.д.
    });
});


Я вызывал посредник из самого модуля в конструкторе:

<?php

namespace App\Http\Terranet\Administrator\Modules;

use Terranet\Administrator\Contracts\Module\Editable;
use Terranet\Administrator\Contracts\Module\Exportable;
use Terranet\Administrator\Contracts\Module\Filtrable;
use Terranet\Administrator\Contracts\Module\Navigable;
use Terranet\Administrator\Contracts\Module\Sortable;
use Terranet\Administrator\Contracts\Module\Validable;
use Terranet\Administrator\Resource;
use Terranet\Administrator\Traits\Module\AllowFormats;
use Terranet\Administrator\Traits\Module\HasFilters;
use Terranet\Administrator\Traits\Module\HasForm;
use Terranet\Administrator\Traits\Module\HasSortable;
use Terranet\Administrator\Traits\Module\ValidatesForm;

/**
 * Administrator Resource Accounts
 *
 * @package Terranet\Administrator
 */
class Accounts extends Resource implements Navigable, Filtrable, Editable, Validable, Sortable, Exportable
{
    use HasFilters, HasForm, HasSortable, ValidatesForm, AllowFormats;

    /**
     * The module Eloquent model
     *
     * @var string
     */

    protected $model = 'reg2005\\PayAssetsLaravel\\Entities\\Accounts';

    public function __construct()
    {
        $this->middleware('OnlyCli');
    }
}

Это вызывало ошибку: Fatal error: Call to undefined method App\Http\Terranet\Administrator\Modules\Accounts::middleware()

Покопавшись в исходниках стало ясно, что middleware это функция абстрактного класса
Illuminate\Routing\Controller, Но я так и не понял как его правильно подключить к модулю.

UPD: Пробовал блокировать доступ через action сервис. Пришлось описывать всевозможные действия: canEdit, canView, canIndex и др. Это успешно заблокировало нужный модуль, но данный подход избыточен. Слишком много писанины, проще уж тогда так:
$this->middleware('OnlyRoot', ['only' => ['canEdit', 'canDelete'] ] );


У меня к вам 2 вопроса:
1. Как правильно расширить класс, что-бы я смог использовать middleware из Module/Action Admin Architect?
2. Есть ли альтернативные пути управления доступом к модулям?
  • Вопрос задан
  • 783 просмотра
Подписаться 2 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 1
@terzi_eduard
День добрый!

Сразу прошу прощения за долгое молчание.

Постараюсь ответить на Ваши вопросы коротко, но содержательно.

На данный момент управлять доступом можно 2-мя способами:

Global permission
файл `config/administrator.php` содержит ключ `permission`, где можно указать глобальные уровни доступа в админку в целом.

'permission' => function ()
{
	return ($auth = auth('admin')->user()) && $auth->isSuperAdmin();
},


Action Service

Каждому модулю (ресурсу) в Admin Architect-е соответствует дефолтный Action Service - класс отвечающий за выполнение CRUD задач. Action Service также служит для объявления групповых задач (bulk actions) и одиночных (single actions).

Как Вы правильно заметили руты (routes) в Admin Architect-е достаточно абстрактные, поэтому есть определенные трудности с навешиванием Middleware для конкретного ресурса. Именно поэтому эта задача и легла на плечи Action Servic-а.

Т.е. чтобы определить уровень доступа для конкретнго action-а в ресурсе - достаточно прописать `can` метод.

public function canEdit(Authenticatable $who, $model)
{
	return $user->isSuperAdmin() && ! $model->isProtected();
}


так же для глобальной политики, можно использовать и сторонний сервис класс:

public function canEdit(Authenticatable $user, $model)
{
	return (new ACLGate($user))->forEntity($model)->checkPermission('edit');
}


еще вариант: каждый Action Service созданный для ресурса можно унаследовать не от `Terranet\Administrator\Services\Actions`, а от своего, нокоего Proxy класса (`php artisan administrator:action `), с определенной логикой ACL, ну или можно попытаться полностью подменить `app('scaffold.actions')` - на свою реализацию.

Итог
Если у вас достаточно гибкая политика ACL - это довольно удобно, хоть и приходится немного дописать, в обычной ситуации можно описать достаточно общие правила примерно так (пример достаточно простой, но отражает суть подхода):

public function __call($method, $args)
{
	if (in_array($method, ['canEdit', 'canActivate', 'canLock'])) {
		list($user, $model) = each($args);
		return $user->isSuperAdmin() && ! $model->isProtected();
	}

	return false;
}


Из плюсов данного подхода по отношению к middleware - для single-action методов (canEdit, canDelete, ваших собственных) позволяет управлять доступом на уровне конкретной записи, а не только ресурса в целом.
К примеру, легко можно запретить удаление пользователя со статусом 'SuperAdmin'.

Надеюсь писал не зря, и это кому-то пригодится!

P.S. Конечно, представленные решения не идеальны, могут и должны быть улучшены, но они выполняют поставленную задачу. На досуге подумаю как можно улучшить данную модель. Если есть предложения - с удовольствием приму на рассмотрение.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы