FlightPHP/Права доступа

Это модуль разрешений, который можно использовать в ваших проектах, если у вас есть несколько ролей в вашем приложении, и каждая роль имеет немного разную функциональность. Этот модуль позволяет определить разрешения для каждой роли, а затем проверить, имеет ли текущий пользователь разрешение на доступ к определенной странице или выполнение определенного действия.

Нажмите сюда для репозитория на GitHub.

Установка

Запустите composer require flightphp/permissions и вы готовы к работе!

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

Сначала вам нужно настроить ваши разрешения, затем сообщить вашему приложению, что означают эти разрешения. В конечном итоге вы проверите ваши разрешения с помощью $Permissions->has(), ->can() или is(). has() и can() имеют одинаковую функциональность, но названы по-разному, чтобы сделать ваш код более читаемым.

Базовый пример

Давайте предположим, что у вас есть функция в вашем приложении, которая проверяет, вошел ли пользователь в систему. Вы можете создать объект разрешений следующим образом:

// index.php
require 'vendor/autoload.php';

// некоторый код

// затем у вас вероятно есть что-то, что говорит вам, какая текущая роль у человека
// скорее всего у вас есть что-то, откуда вы извлекаете текущую роль
// из переменной сеанса, которая определяет это
// после входа в систему у кого-то должна быть роль 'guest' или 'public'.
$current_role = 'admin';

// настройка разрешений
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
    return $current_role !== 'guest';
});

// Вам вероятно захочется сохранить этот объект где-то в Flight
Flight::set('permission', $permission);

Затем в контроллере где-то вы можете иметь что-то вроде этого.

<?php

// некоторый контроллер
class SomeController {
    public function someAction() {
        $permission = Flight::get('permission');
        if ($permission->has('loggedIn')) {
            // сделать что-то
        } else {
            // сделать что-то другое
        }
    }
}

Вы также можете использовать это для отслеживания, есть ли у них разрешение на выполнение определенного действия в вашем приложении. Например, если у вас есть способ, как пользователи могут взаимодействовать с публикацией в вашем программном обеспечении, вы можете проверить, имеют ли они разрешение на выполнение определенных действий.

$current_role = 'admin';

// настройка разрешений
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
    if($current_role === 'admin') {
        $permissions = ['create', 'read', 'update', 'delete'];
    } else if($current_role === 'editor') {
        $permissions = ['create', 'read', 'update'];
    } else if($current_role === 'author') {
        $permissions = ['create', 'read'];
    } else if($current_role === 'contributor') {
        $permissions = ['create'];
    } else {
        $permissions = [];
    }
    return $permissions;
});
Flight::set('permission', $permission);

Затем где-то в контроллере...

class PostController {
    public function create() {
        $permission = Flight::get('permission');
        if ($permission->can('post.create')) {
            // сделать что-то
        } else {
            // сделать что-то еще
        }
    }
}

Внедрение зависимостей

Вы можете внедрять зависимости в замыкание, которое определяет разрешения. Это полезно, если у вас есть какой-то переключатель, идентификатор или любая другая точка данных, которую вы хотите проверить. То же самое работает для вызовов вида Class->Method, за исключением того, что аргументы определяются в методе.

Замыкания

$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
    // ... код
});

// в вашем файле контроллера
public function createOrder() {
    $MyDependency = Flight::myDependency();
    $permission = Flight::get('permission');
    if ($permission->can('order.create', $MyDependency)) {
        // сделать что-то
    } else {
        // сделать что-то еще
    }
}

Классы

namespace MyApp;

class Permissions {

    public function order(string $current_role, MyDependency $MyDependency = null) {
        // ... код
    }
}

Сокращение для установки разрешений с использованием классов

Вы также можете использовать классы для определения ваших разрешений. Это полезно, если у вас много разрешений, и вы хотите, чтобы ваш код был чистым. Вы можете сделать что-то вроде этого:

<?php

// код инициализации
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');

// myapp/Permissions.php
namespace MyApp;

class Permissions {

    public function order(string $current_role, int $user_id) {
        // Предположим, что вы это настроили заранее
        /** @var \flight\database\PdoWrapper $db */
        $db = Flight::db();
        $allowed_permissions = [ 'read' ]; // каждый может просматривать заказ
        if($current_role === 'manager') {
            $allowed_permissions[] = 'create'; // менеджеры могут создавать заказы
        }
        $some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
        if($some_special_toggle_from_db) {
            $allowed_permissions[] = 'update'; // если у пользователя есть особый переключатель, он может обновлять заказы
        }
        if($current_role === 'admin') {
            $allowed_permissions[] = 'delete'; // администраторы могут удалять заказы
        }
        return $allowed_permissions;
    }
}

Здесь примечательно то, что есть также сокращение, которое можно использовать (которое также может быть кешировано!!!), где вы просто говорите классу разрешений сопоставить все методы в классе в разрешения. Поэтому, если у вас есть метод с именем order() и метод с именем company(), они будут автоматически сопоставлены, и вы сможете просто выполнить $Permissions->has('order.read') или $Permissions->has('company.read'), и это сработает. Определение этого очень сложно, так что держитесь здесь со мной. Просто вам нужно сделать это:

Создайте класс разрешений, которые вы хотите сгруппировать вместе.

class MyPermissions {
    public function order(string $current_role, int $order_id = 0): array {
        // код определения разрешений
        return $permissions_array;
    }

    public function company(string $current_role, int $company_id): array {
        // код определения разрешений
        return $permissions_array;
    }
}

Затем сделайте разрешения обнаруживаемыми с использованием этой библиотеки.

$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);

Наконец, вызовите разрешение в вашей кодовой базе, чтобы проверить, разрешено ли пользователю выполнение заданного разрешения.

class SomeController {
    public function createOrder() {
        if(Flight::get('permissions')->can('order.create') === false) {
            die('Вы не можете создать заказ. Извините!');
        }
    }
}

Кеширование

Для включения кэширования, см. простую библиотеку wruczak/phpfilecache. Пример включения приведен ниже.


// этот $app может быть частью вашего кода, или
// вы можете просто передать null, и он извлечет из Flight::app() в конструкторе
$app = Flight::app();

// Теперь для этого принимается файловое кэширование. Другие могут легко
// быть добавлены в будущем. 
$Cache = new Wruczek\PhpFileCache\PhpFileCache;

$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // 3600 - это сколько секунд кэшировать это. Оставьте это, чтобы не использовать кэширование

И впереди!