Hướng dẫn Access Control List sử dụng database trong CakePHP 2.7.2

Chào các bạn,

Sau bài [Cakephp] – Hướng dẫn Access Control List sử dụng INI thì đến bài này chúng ta sẽ cùng tìm hiểu phần nâng cao hơn, sử dụng Database để quản lý phân quyền truy cập.

Tham khảo:

Một số yêu cầu:

  1. Source code bài mới nhất trong series CakePHP của mình bao gồm cả database. Tại đây nút Download
  2. Download source về chạy nếu có lỗi permission về thư mục thì phần quyền lại thư mục app/tmp
  3. Đều cần thiết của bài này là cần các bạn chạy được Console của CakePHP, mình cố gắng không chạy nó nhưng các bước cuối thì phải chạy thôi, làm code lâu lắm. Nhưng chạy chỉ khoảng 2 lần thôi cũng rất đơn giản.

Sau đây là các bước thực hiện:

Bước 1: Chuẩn bị Database.

Trong Source tải về đã có sẵn bảng users, cần thêm một trường trong bảng này, chạy lệnh SQL sau:

ALTER TABLE `users`  ADD `group_id` INT(11) NOT NULL DEFAULT '1'  AFTER `date_updated`;

Tiếp theo chúng ta thêm bảng Groups:

CREATE TABLE groups (
 id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(100) NOT NULL,
 created DATETIME,
 modified DATETIME
);

Nếu các bạn muốn quản lý truyện do user nào tạo ra thì thêm đoạn sau(tuỳ chọn ko bắt buộc):

ALTER TABLE `stories`  ADD `user_id` INT(11) NOT NULL DEFAULT '1'  AFTER `id`;

Bước 2: Trên là công đoạn update database để phù hợp thôi chứ chưa có database phần quyền. Chúng ta cần tạo các Controller, View, Model tương ứng nữa, trong source sẽ có, mình sẽ nói qua tý.

Thứ nhất là tạo ra các file

  • app/Controller/GroupsController.php
  • app/Model/Group.php
  • app/View/Groups/admin_add.ctp
  • app/View/Groups/admin_edit.ctp
  • app/View/Groups/admin_index.ctp

Phần code các bạn tự làm hoặc trong source luôn nhá.

Thứ hai trong /app/Model/User.php

Tìm đoạn:


'email' => array(
'valid email' => array(
'rule' => 'email',
'message' => 'Nhập địa chỉ email hợp lệ'
),
'duplicate email' => array(
'rule'=>'isUnique',
'message' => 'email đã có người sử dụng'
)
),

thay thế bằng:


'email' => array(
'valid email' => array(
'on' => 'create',
'rule' => 'email',
'message' => 'Nhập địa chỉ email hợp lệ'
),
'duplicate email' => array(
'on' => 'create',
'rule'=>'isUnique',
'message' => 'email đã có người sử dụng'
)
),

thêm ‘on’ => ‘create’ là để kiểm tra email đó khi tạo thui ko kiểm tra khi update, mình không kiểm tra nó để tiết kiệm thời gian.

Thêm vào trước hàm beforeSave():


public $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);

Thứ 3 /app/View/Users/admin_add.ctp, thêm vào một select để chọn group làm tương tự cho /app/View/Users/admin_edit.ctp, chỉnh sửa các function tương ứng trong UsersController.php

Test lại các bước trên bằng cách thêm vào 3 Group:

  • administrators
  • managers
  • users

Sau đó edit các user đang có để chọn group tương ứng với 3 group trên. Sau khi ổn các bạn hãy qua Bước 3.

Bước 3: Chỉnh sửa file app/Controller/AppController.php, thay thế thành như sau:


class AppController extends Controller {
public $components = array(
'Acl',
'Data',
'Session',
'Auth'=>array(
'authenticate' => array(
'Blowfish' => array(
'userModel' => 'User',
)
),
'loginAction' => array('admin'=>true, 'controller'=>'users', 'action'=>'login'),
'loginRedirect' => array('admin'=>true, 'controller'=>'dashboards', 'action'=>'index'),
'logoutRedirect' => array('admin'=>true, 'controller'=>'users', 'action'=>'login'),
'authError' => 'You can not access that page',
'authorize' => array(
'Actions' => array('actionPath' => 'controllers')
)
)
);
public function beforeFilter(){
if($this->params['prefix'] == "admin"){
if($this->Auth->loggedIn()){
}
}else{
$this->Auth->allow();
}
$this->set('current_user', $this->Auth->user());
}
}

Nội dung cũng không có gì mới, thêm Acl Component để chạy trên toàn controller, còn phần ‘Actions’ => array(‘actionPath’ => ‘controllers’), chữ controllers nó là alias cho ACO gốc mục đích là dùng nó để cấp quyền cho toàn bộ các node con bên trong nó, phần chạy lệnh Console sẽ sử dụng nó.

Vì cần cho user đăng nhập để thực hiện các thao tác nên tạm thời chúng ta sẽ thêm $this->Auth->allow();

vào trong hàm beforeFilter của UsersController.php và GroupsController.php, sau khi phân quyền xong chúng ta sẽ xoá nó đi.

Bước 4: Tạo database phân quyền(ACL)

Phần này sẽ tạo các bảng cho phân quyền, tiến hành chạy các câu SQL sau:


CREATE TABLE acos (
id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
parent_id INTEGER(10) DEFAULT NULL,
model VARCHAR(255) DEFAULT '',
foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
alias VARCHAR(255) DEFAULT '',
lft INTEGER(10) DEFAULT NULL,
rght INTEGER(10) DEFAULT NULL,
PRIMARY KEY (id)
);

CREATE TABLE aros_acos (
id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
aro_id INTEGER(10) UNSIGNED NOT NULL,
aco_id INTEGER(10) UNSIGNED NOT NULL,
_create CHAR(2) NOT NULL DEFAULT 0,
_read CHAR(2) NOT NULL DEFAULT 0,
_update CHAR(2) NOT NULL DEFAULT 0,
_delete CHAR(2) NOT NULL DEFAULT 0,
PRIMARY KEY(id)
);

CREATE TABLE aros (
id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
parent_id INTEGER(10) DEFAULT NULL,
model VARCHAR(255) DEFAULT '',
foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
alias VARCHAR(255) DEFAULT '',
lft INTEGER(10) DEFAULT NULL,
rght INTEGER(10) DEFAULT NULL,
PRIMARY KEY (id)
);

– Trong đó:

  • aros: chứa model ứng với từng group
  • acos: chứa controller và toàn bộ action của controller đó. Trong đó có thành phần controllers, giữ vai trò là cha của các controller, nếu group nào có quyền trên controllers thì mặc nhiên có quyền trên toàn bộ các action của các cotroller khác
  • aros_acos: bảng phân quyền chi tiết nó sẽ liên kết 2 bảng trên.

Bước 5: Thiết lập ACL

Sử dụng AclBehavior để liên kết group và user vào  Acl

– Mục đích: liên kết group và user vào Acl. Khi chúng ta tạo group và user, dữ liệu cũng sẽ được đưa vào bảng aros
– Chèn code sau vào model User.php:

public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
if (isset($this->data['User']['group_id'])) {
$groupId = $this->data['User']['group_id'];
} else {
$groupId = $this->field('group_id');
}
if (!$groupId) {
return null;
}
return array('Group' => array('id' => $groupId));
}

Bên trong Group.php model chúng ta cũng thêm đoạn sau:


public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
return null;
}

Đến đây thì các liên kết giữa user và group trong ACL đã được thiết lập, nhưng có vấn đề là trong bảng usersgroups đã có dữ liệu, nhưng dữ liệu này chưa được liên kết. Để xử lý thì chúng ta sẽ tạo các dữ liệu mới gồm cả Group và User, cứ việc tạo ra sau đó chúng ta sẽ xoá dữ liệu cũ rồi edit các dữ liệu mới thêm vào  thành mới xoá đi (Đơn giản là a: dữ liệu cũ, b: dữ liệu mới, xoá a đi, edit b thành a).

Đoạn trên mà các bạn chưa hiểu ý thì cứ vô database empty 2 bảng users, groups rồi tạo lại bằng giao diện admin, bắt buộc nhớ đăng nhập trang admin trước rồi nãy empty nhá.

Sau khi thêm lại các dữ liệu Users và Groups, các bạn xem dữ liệu trong bảng aros sẽ được như sau:

 

+----+-----------+-------+-------------+-------+------+------+
| id | parent_id | model | foreign_key | alias | lft  | rght |
+----+-----------+-------+-------------+-------+------+------+
|  1 |      NULL | Group |           1 | NULL  |    1 |    4 |
|  2 |      NULL | Group |           2 | NULL  |    5 |    8 |
|  3 |      NULL | Group |           3 | NULL  |    9 |   12 |
|  4 |         1 | User  |           1 | NULL  |    2 |    3 |
|  5 |         2 | User  |           2 | NULL  |    6 |    7 |
|  6 |         3 | User  |           3 | NULL  |   10 |   11 |
+----+-----------+-------+-------------+-------+------+------+
6 rows in set (0.00 sec)

Ở đây mình phân quyền ở mức Group nghĩa là group có quyền nào thì user thuộc group đó sẽ có quyền như group, ví dụ này mình không phân quyền ở mức user nhá, để thưc hiện phân quyền Group chúng ta sẽ thêm đoạn sau vào app/Model/User.php


public function bindNode($user) {
return array('model' => 'Group', 'foreign_key' => $user['User']['group_id']);
}

và sử đoạn sau:


public $actsAs = array('Acl' => array('type' => 'requester'));

thành đoạn:


public $actsAs = array('Acl' => array('type' => 'requester', 'enabled' => false));

Đoạn trên sẽ vô hiệu hoá việc insert liên kết của user và bảng aros, giờ nếu bạn add thêm một user thì sẽ thấy không có thêm dòng nào trong bảng aros nữa, đó là vì ACL đã không còn kiểm tra đến User AROs nữa.

Đến đây mình yêu cầu các bạn tiếp tục empty 2 bảng usersgroups một lần nữa, thêm bảng aros luôn. Rồi tạo lại dữ liệu nữa.

Sau khi tạo xong dữ liêu aros sẽ như sau: không còn User nữa.

+----+-----------+-------+-------------+-------+------+------+
| id | parent_id | model | foreign_key | alias | lft  | rght |
+----+-----------+-------+-------------+-------+------+------+
|  1 |      NULL | Group |           1 | NULL  |    1 |    2 |
|  2 |      NULL | Group |           2 | NULL  |    3 |    4 |
|  3 |      NULL | Group |           3 | NULL  |    5 |    6 |
+----+-----------+-------+-------------+-------+------+------+
3 rows in set (0.00 sec)

Bước 6: Tạo ra các ACOs.

Như các bạn đã thấy thì khi chúng ta tạo các Groups và Users thì AROs sẽ tự động được tạo ra nhưng với ACOs thì không làm được vậy. Có 2 cách để tạo ra ACOs : một là dùng cake shell, hai là dùng AclComponent.  Tạo ra một ACO gốc là controllers bởi vì đoạn code bên trong AppControllers chúng ta đã khai báo ‘actionPath’ => ‘controllers’ trong biến $component. Đến đây sẽ dùng Console cake shell để tạo nha:

Đầu tiên mở termianl hoặc cmd lên di chuyển dấu nháy chuột vào thư mục app của project chúng ta.

chạy lệnh sau: ./Console/cake acl create aco root controllers

Sau khi chạy lệnh đó sẽ được như sau:

cakephp acl create aco root controllers

Xuất hiện dòng New Aco ‘controllers’ created là các bạn thành công.(đừng tắt đi nhá sẽ chạy 1 lệnh nữa)

Kiểm tra bảng acos trong database sẽ xuất hiện 1 record có alias là controlles

Bước 7: Tải plugin AclExtras , sau khi tải xong được thư mục AclExtras (đổi tên lại nếu không đúng) rồi copy vào /app/Plugin, rồi mở file /app/Config/bootstrap.php lên thêm đoạn sau vào:


CakePlugin::load('AclExtras');

Mở lại Console cake shell và chạy: ./Console/cake AclExtras.AclExtras aco_sync

Sau khi chạy đoạn trên sẽ xuất hiện:

Acl CakePHP AclExtras

Nhiệm vụ của đoạn lệnh trên là nó sẽ quét toàn bộ các Controllers, function trong controller tiến hành insert vào bảng acos. Các bạn có thể tắt Console cake shell rồi.

Muốn kiểm tra các bạn có thể vào bảng acos để kiểm tra lại.

Đến bước này thì 3 bảng dữ liệu của ACL đã có 2 bảng acos, aros có dữ liệu, việc tiếp theo là phân quyền chi tiết cho các group để nó tự động thêm vào bảng aros_acos

Bước 8: Thiết lập quyền:

Cú pháp thiết lâp:

  • Cho phép: $this->Acl->allow($aroAlias, $acoAlias);
  • Không cho phép: $this->Acl->deny($aroAlias, $acoAlias);

Để chạy thiết lập quyền chúng ta sẽ tạo một action trong bất cứ controller nào các bạn tuỳ chọn và sau đó sẽ chạy nó ở url trình duyệt. Mình đặt funtion là admin_initDB() tương ứng mình sẽ chạy url là http://doctruyen.local/admin/users/initdb Nội dung của funtion này là:


public function admin_initDB() {
$group = $this->User->Group;

// Allow admins to everything
$group->id = 1;
$this->Acl->allow($group, 'controllers');

// allow managers to Categories and Stories
$group->id = 2;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Categories');
$this->Acl->allow($group, 'controllers/Stories');

// allow users to only add and edit on stories and slides
$group->id = 3;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Stories/admin_add');
$this->Acl->allow($group, 'controllers/Stories/admin_edit');
$this->Acl->allow($group, 'controllers/Slides/admin_add');
$this->Acl->allow($group, 'controllers/Slides/admin_edit');

// allow basic users to log out
$this->Acl->allow($group, 'controllers/users/admin_logout');

echo "all done";
exit;
}

Phần trên sẽ như sau:

Group 1: Admin có full quyền trên tất cả controller

Group 2: Managers  có full quyền cho controller Categories và Stories

Group 3: Users có quyền add và edit trên Stories và Slides

Tuỳ theo nhu cầu của các bạn hãy thiết lập thêm các quyền cho group. Nếu tạo group mới thì add user và group mới đó rồi tiếp tục thiết lập quyền trong acction này.

Hãy ẩn $this->Auth->allow(); ở đầu file UsersController.php và GroupsController.php đi(lúc đầu thêm á).

Nếu thiết lập xong rồi thì tiến hành chạy url trên, rồi vào bảng aros_acos kiểm tra dữ liệu sẽ được thêm vào. Trường hợp chạy url trên mà không được(bị lỗi quyền ) thì thêm $this->Auth->allow(‘admin_initDB’); vào function beforeFilter() của UsersController.php

Trường hợp chạy thành công thì chúng ta sẽ tiến hành test. Tiến hành đăng nhập từng user theo từng group đã phân quyền, nếu đăng nhập Administrators thì phải truy cập vào được tất cả các page, nếu đăng nhập bằng managers sẽ tự động vào controller được phân quyền và có tất cả quyền trên controller đó, nếu vào controller khác không được phân quyền nó sẽ tự động chuyển đến trang trước đó, còn users sẽ vào được các action được chỉ định , nếu vào action khác cũng sẽ chuyển đến action trước đó.

Khi các bạn đưa nó lên hosting để chạy thì hãy xoá hoặc funtion admin_initDB() đi nhá, khi nào phân quyền thì thêm vào lại, đừng để trên hosting.

OK đến đây thì đã xong phần hướng dẫn phân quyền ACL bằng database, mình làm theo project truyên tranh nên có khi không giống với các dự án của các bạn nhưng trên cơ bản sẽ giống vậy. Hy vọng sẽ giúp được, nếu có phản hồi hãy comment bên dưới nhá

Kết luận

  1. Nếu có thắc mắc gì các bạn để lại comment bên dưới mình sẽ trả lời sớm nhất có thể.
  2. Cảm ơn các bạn đã đọc.

Nongdanit.info
[Cakephp] – Hướng dẫn Access Control List sử dụng database