ACL (Access Control List) Authorization in Laravel 5.1

laravel-logo-white.png

1. Giới thiệu về ACL trong Laravel 5.1

Authorization được đưa vào laravel từ phiên bản 5.1.11. Từ phiên bản này, ALC của laravel được gọi là Gate. Bằng việc sử dụng Gate, chúng ta có thể dễ dàng kiểm tra nếu 1 user (đã login hay 1 user đặc biệt nào đó) có "được phép" - allow thực thi một hành động nào đó hay không một cách đơn giản.

if (Gate::denies('update-contact', $contact)) {
    abort(403);
}

2. Cách thức hoạt động của ACL

- Defining Abilities

Hãy bắt đầu bằng cách định nghĩa một ability (khả năng) trong provider mặc định AuthServiceProvider:

...
class AuthServiceProvider extends ServiceProvider
{
    	public function boot(GateContract $gate) {
        parent::registerPolicies($gate);
        $gate->define('update-contact', function ($user, $contact) {
        	return $user->id === $contact->user_id;
        });
    }
}

Như chúng ta thấy, tham số đầu tiên được đưa vào là user. Nếu chưa có user nào login thì Gate luôn trả về giá trị false cho tất cả các trường hợp. Ngoài cách trên, chúng ta có thể truyển vào class name và method tương ứng vào tương tự như khi khai báo route trong laravel.

$gate->define('update-post', '[email protected]');

- Checking with Façades

Gate cho phép chúng ta kiểm tra bằng cách hàm: check, allows hay denies (check tương tự như allows, trả về cùng 1 kết quả, còn denies thì ngược lại). Nếu chúng ta sử dụng façade, chúng ta không cần phải truyền vào tham số user, façade sẽ tự động truyền user đang login như tham số user:

if (Gate::denies('update-contact', $contact)) {
	abort(403);
}
if (Gate::allows('create-contact')) {
	redirect('hooray');
}

Nếu chúng ta sử dụng Ability với nhiều tham số:

$gate->define('delete-interaction', function ($user, $contact, $interaction) {
	// Do stuff...
});

if (Gate::allows('delete-interaction', [$contact, $interaction]) {
	// Do stuff...
});

Nếu chúng ta muốn kiểm tra "quyền" của 1 user đặc biệt nào đó thì có thể sử dụng Gate::forUser($user) thay thế cho Gate ở các ví dụ trên

- Checking on the user model

Từ phiên bản 5.1.* trở đi, model Laravel's App\User cung cấp thêm 2 hàm cancannot, tương ứng với allowsdenies trong Gate. Để sử dụng được chúng ta phải khai báo model đó Authorizable. Sau đó chúng ta có thể sử dụng cancannot ở bất cứ đâu, trong controller, view,...

if ($user->can('update-contact', $contact)) {
	// Do stuff
}

- Checking in Blade

Bằng việc khai báo model Authorizable, chúng ta có thể sử dụng gatengay trong blade

<nav>
    <a href="/">Home</a>
    @can('edit-contact', $contact)
    <a href="{{ route('contacts.edit', [$contact->id]) }}">Edit This Contact</a>
    @endcan
</nav>

- Intercepting checks

Trong một số trường hợp chúng ta có thể chặn việc kiểm tra trong gate bằng hàm before, hàm này sẽ trả về giá trị trước tất cả các bước check

$gate->before(function ($user, $ability) {
    if ($user->isOwner()) {
    	return true;
    }
});

- Policies

Ngoài cách viết các abilities vào trong provider mặc định là AuthServiceProvider, chúng ta có thể viết các abilities vào các Policy tương ứng để dễ quản lý hơn.

php artisan make:policy ContactPolicy

Sau khi tạo thành công Policy, chúng ta khai báo nó trong: AuthServiceProvider

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
    	Contact::class => ContactPolicy::class,
	];

Để tạo ability update cho model Contact, chúng ta viết nó vào ContractPolicy vừa tạo ở trên.

<?php

namespace App\Policies;

class ContactPolicy
{
    public function update($user, $contact) {
    	return $user->id === $contact->user_id;
    }
}

- Controller authorization

Ngoài các cách kiểm tra authorize thông thường, chúng ta có thể sử dụng gate ngay trong controller. Gate sẽ throw exception 403 nếu authorization fails.

public function update($id)
{
	$contact = Contact::findOrFail($id);
	$this->authorize('update', $contact);
	// Do stuff...
}

3. Kết luận

Với việc kết hợp gate một cách hợp lý, việc xử lý các câu lệnh phức tạp sẽ ngắn gọn hơn rất nhiều, đồng thời thể hiện rõ hơn hành động trong cách đặt tên cho mỗi ability, code của chúng ta sẽ sáng sủa và dễ hiểu hơn rất nhiều.


All Rights Reserved