การใช้งาน Laratrust ตัวจัดการเรื่องสิทธิ์การใช้งาน (ACL)

Ask by Save Pongsiri 1 year ago

หลังจากบทความที่แล้วได้เขียนถึงการติดตั้ง Laratust และตั้งค่ากันเรียบร้อยแล้ว บทความนี้ก็เลยต่อเนื่องมาเขียนถึงวิธีใช้งาน Laratrust ทั้งหมดเลยครับ

ก่อนจะไปพูดถึงการใช้งาน Laratrust สิ่งที่เราต้องรู้จักก่อนก็คือความหมายของแต่ละคำที่เราจะเจอก่อน
1. Role คือ ชื่อของบทบาทหน้าที่โดยรวม เช่น admin
2. Permission คือ ชื่อของสิทธิ์ที่มีในแต่ละบทบาท เช่น admin นั้นมีสิทธิ์ในการทำอะไรได้บ้าง เช่น 'create-user', 'update-post' เป็นต้น

การสร้างและใช้งาน Role, Permission


การสร้าง Role

$owner = new Role();
$owner->name         = 'owner';
$owner->display_name = 'Project Owner';
$owner->description  = 'User is the owner of a given project';
$owner->save();
$admin = new Role();
$admin->name         = 'admin';
$admin->display_name = 'User Administrator';
$admin->description  = 'User is allowed to manage and edit other users';
$admin->save();


การสร้าง Permission 

$createPost = new Permission();
$createPost->name         = 'create-post';
$createPost->display_name = 'Create Posts';
$createPost->description  = 'create new blog posts';
$createPost->save();
$editUser = new Permission();
$editUser->name         = 'edit-user';
$editUser->display_name = 'Edit Users';
$editUser->description  = 'edit existing users';
$editUser->save();


การเพิ่ม Permission ให้กับแต่ละ Role

$admin->attachPermission($createPost);
//หรือ $admin->permissions()->attach([$createPost->id]);
$owner->attachPermissions([$createPost, $editUser]);
//หรือ $owner->permissions()->attach([$createPost->id, $editUser->id]);
$owner->syncPermissions([$createPost, $editUser]);
//หรือ $owner->permissions()->sync([$createPost->id, $editUser->id]);


การลบ Permission ออกจาก Role

$admin->detachPermission($createPost);
//หรือ $admin->permissions()->detach([$createPost->id]);
$owner->detachPermissions([$createPost, $editUser]);
//หรือ $owner->permissions()->detach([$createPost->id, $editUser->id]);


การเพิ่ม Role ให้กับ User แต่ละคน

$user->attachRole($admin);
//หรือ $user->roles()->attach([$admin->id]);
$user->attachRoles([$admin, $owner]);
//หรือ $user->roles()->attach([$admin->id, $owner->id]);
$user->syncRoles([$admin->id, $owner->id]);
//หรือ $user->roles()->sync([$admin->id, $owner->id]);
$user->syncRolesWithoutDetaching([$admin->id, $owner->id]);
//หรือ $user->roles()->syncWithoutDetaching([$admin->id, $owner->id]);


การลบ Role ออกจาก User แต่ละคน

$user->detachRole($admin);
//หรือ $user->roles()->detach([$admin->id]);
$user->detachRoles([$admin, $owner]);
//หรือ $user->roles()->detach([$admin->id, $owner->id]);


การเพิ่ม Permission ให้กับ User แต่ละคน

$user->attachPermission($editUser);
//หรือ $user->permissions()->attach([$editUser->id]);
$user->attachPermissions([$editUser, $createPost]);
//หรือ $user->permissions()->attach([$editUser->id, $createPost->id]);
$user->syncPermissions([$editUser->id, $createPost->id]);
//หรือ $user->permissions()->sync([$editUser->id, createPost->id]);
$user->syncPermissionsWithoutDetaching([$editUser, $createPost]);
//หรือ $user->permissions()->syncWithoutDetaching([$createPost->id, $editUser->id]);


การลบ Permission ออกจาก User แต่ละคน

$user->detachPermission($createPost);
//หรือ $user->roles()->detach([$createPost->id]);
$user->detachPermissions([$createPost, $editUser]);
//หรือ $user->roles()->detach([$createPost->id, $editUser->id]);


การเช็ค Role และสิทธิ์ของผู้ใช้แต่ละคน

ให้ใช้ method จากตัวแปล $user ที่เราต้อง หรือ auth() ที่กำลัง login อยู่ตามตัวอย่าง

$user->hasRole('owner');   // false
$user->hasRole('admin');   // true
$user->can('edit-user');   // false
$user->can('create-post'); // true

ทั้ง can() และ hasRole() สามารถเช็คทีเดียวได้หลายค่าด้วยอาเรย์ครับ

$user->hasRole(['owner', 'admin']);       // true
$user->can(['edit-user', 'create-post']); // true
$user->hasRole('owner|admin');       // true
$user->can('edit-user|create-post'); // true

และถ้าเราต้องการให้เช็คว่าต้องเข้าเงื่อนไขทั้งหมดเลยก็ให้เติม true ลงไปในพารามิเตอร์ที่ 2

$user->hasRole(['owner', 'admin']);             // true
$user->hasRole(['owner', 'admin'], true);       // false, กรณี user ไม่ได้เป็น admin
$user->can(['edit-user', 'create-post']);       // true
$user->can(['edit-user', 'create-post'], true); // false, กรณี user ไม่มีสิทธิ์ edit-user

ที่พิเศษกว่านั้นคือเราสามารถเช็คแบบกว้าง ๆ ได้แบบนี้ด้วย

// match any admin permission
$user->can('admin.*'); // true
// match any permission about users
$user->can('*_users'); // true

และ can() สามารถเรียกแบบสวยแบบนี้ก็ได้

$user->canCreateUsers();
// เหมือนกับ $user->can('create-users');


เช็คด้วย ability()

ฟังก์ชั่นนี้ใช้เช็ครวดเดียวเลยทั้ง Role, Permission ของผู้ใช้ การใช้งานมี 3 พารามิเตอร์ คือ (roles, permissions, options)

โดยเราสามารถเช็คได้หลายเงื่อนไขด้วยการใช้ | ในการแบ่งคำ หรือเขียนแบบอาเรย์ก็ได้

$user->ability(['admin', 'owner'], ['create-post', 'edit-user']);
// หรือ
$user->ability('admin|owner', 'create-post|edit-user');

พารามิเตอร์ที่ 3 เป็นอาเรย์ options 

$options = [
    'validate_all' => true | false (Default: false),
    'return_type'  => boolean | array | both (Default: boolean)
];

- validate_all    คือต้องเข้าเงื่อนไขทั้งหมด (ค่าเริ่มต้นเป็น false)
- return_type    เซ็ตว่าจะให้ return ค่ากลับมาเป็นแบบ boolean หรือ array (ค่าเริ่มต้นเป็น boolean)

ตัวอย่างการใช้งาน

$options = [
    'validate_all' => true,
    'return_type' => 'both'
];
list($validate, $allValidations) = $user->ability(
    ['admin', 'owner'],
    ['create-post', 'edit-user'],
    $options
);
var_dump($validate);
// bool(false)
var_dump($allValidations);
// array(4) {
//     ['role'] => bool(true)
//     ['role_2'] => bool(false)
//     ['create-post'] => bool(true)
//     ['edit-user'] => bool(false)
// }


การเรียกข้อมูลที่สัมพันธ์กัน

ถ้าเราต้องการดึงสิทธิ์ของ user มาทั้งหมด สามารถทำได้โดยใช้เมธอด allPermissions จะได้ permission ทั้งหมดที่ user คนนั้นมีจากทุก roles ด้วย

dump($user->allPermissions());
/*
 Illuminate\Database\Eloquent\Collection {#646
  #items: array:2 [
    0 => App\Permission {#662
      ...
      #attributes: array:6 [
        "id" => "1"
        "name" => "edit-users"
        "display_name" => "Edit Users"
        "description" => null
        "created_at" => "2017-06-19 04:58:30"
        "updated_at" => "2017-06-19 04:58:30"
      ]
      ...
    }
    1 => App\Permission {#667
      ...
      #attributes: array:6 [
        "id" => "2"
        "name" => "manage-users"
        "display_name" => "Manage Users"
        "description" => null
        "created_at" => "2017-06-19 04:58:30"
        "updated_at" => "2017-06-19 04:58:30"
      ]
      ...
    }
  ]
}
 */


ถ้าเราต้องการดูรายชื่อ user ที่มี role บางอย่าง เช่น admin สามารถทำได้โดยการใช้ query scope ชื่อ whereRoleIs()

$users = User::whereRoleIs('admin')->get();

ดู user ที่มี permissions บางอย่างได้ด้วยเช่นกัน ด้วย scope ชื่อ wherePermissionIs()

$users = User::wherePermissionIs('edit-user')->get();


เช็คว่าเป็นเจ้าของมั้ย

สมมติว่าต้องการเช็คว่า user นี้เป็นเจ้าของ post ที่จะแก้ไขหรือเปล่า ก็สามารถทำได้ครับ โดยใช้ฟังก์ชั่น owns()

public function update (Post $post) {
    if ($user->owns($post)) { //บรรทัดนี้จะตรวจว่ามี 'user_id' อยู่ใน $post หรือไม่
       abort(403);
    }
    ...
}

ถ้าในตาราง posts เราใช้ key ของเจ้าของเป็นชื่ออื่นที่ไม่ใช่ user_id เราสามารถระบุชื่อ filed ของเราเองในแอททรีบิวต์ที่ 2

$user->owns($post, 'idUser');


เช็คสิทธิ์และความเป็นเจ้าของ

ถ้าเราต้องการเช็คสิทธ์พร้อมกับเช็คด้วยว่า user เป็นเจ้าของ object นั้นหรือเปล่า เราสามารถทำได้โดยการใช้ method ชื่อว่า canAndOwns และ hasRoleAndOwns

ทั้ง 2 method จะมี 3 พารามิเตอร์
permission หรือ role คือเช็คว่ามีสิทธิ์หรือมี role นั้นมั้ย (เช็คหลายเงื่อนไขก็ใช้เป็นอาเรย์)
thing คือ object ที่ใช้เพื่อเช็คความเป็นเจ้าของ
options จะใช้กรณีที่ต้องการเช็คทั้งหมดหรือการเปลี่ยนชื่อ foreign key ที่ใช้เช็ค

นี่คือตัวอย่างการใช้งานทั้ง 2 method

$post = Post::find(1);
$user->canAndOwns('edit-post', $post);
$user->canAndOwns(['edit-post', 'delete-post'], $post);
$user->canAndOwns(['edit-post', 'delete-post'], $post, ['requireAll' => false, 'foreignKeyName' => 'writer_id']);
$user->hasRoleAndOwns('admin', $post);
$user->hasRoleAndOwns(['admin', 'writer'], $post);
$user->hasRoleAndOwns(['admin', 'writer'], $post, ['requireAll' => false, 'foreignKeyName' => 'writer_id']);

คลาสของ Laratrust มีทางลัดที่จะใช้กับ method อย่าง owns(), canAndOwns และ hasRoleAndOwns เพื่อเช็คกับผู้ใช้ที่เข้าใช้งานอยู่ตอนนี้ได้

Laratrust::owns($post);
Laratrust::owns($post, 'idUser');
Laratrust::canAndOwns('edit-post', $post);
Laratrust::canAndOwns(['edit-post', 'delete-post'], $post, ['requireAll' => false, 'foreignKeyName' => 'writer_id']);
Laratrust::hasRoleAndOwns('admin', $post);
Laratrust::hasRoleAndOwns(['admin', 'writer'], $post, ['requireAll' => false, 'foreignKeyName' => 'writer_id']);


Teams

สำหรับวิธีการกำหนด Roles และการลบ Roles ของแบบ team ก็ใช้ method เดียวกับของ user เลยครับ แต่เราต้องระบุพารามิเตอร์สำหรับของ team เพิ่มเข้าไปด้วย

$team = Team::where('name', 'my-awesome-team')->first();
$admin = Role::where('name', 'admin')->first(); $owner = Role::where('name', 'owner')->first();
$user->attachRole($admin, $team); //กำหนดทีละ Role $user->attachRole([$admin, $owner], $team); //กำหนดแบบหลาย Role // พารามิเตอร์สามารถใส่เป็น object, array หรือชื่อ Role เลยก็ได้

จากตัวอย่างด้านบน เป็นการตั้งให้ user เป็น admin แต่อยู่เฉพาะในทีม "my-awesome-team" เท่านั้น (เราสามารถตั้งให้ user เป็นได้หลาย role โดยการใส่พารามิเตอร์ role แบบอาเรย์)

การลบ role สามารถทำได้โดยใช้ method ที่ว่า detachRole สำหรับลบทีละ Role และ detachRoles สำหรับหลายลบที่เดียวหลาย Role

$user->detachRole($admin, $team); 
$user->detachRoles([$admin, $owner], $team); // พารามิเตอร์สามารถใส่เป็น object, array หรือชื่อ Role เลยก็ได้


และเราสามารถซิงค์ Roles ภายในทีมได้ด้วย

$user->syncRoles([$admin, $owner], $team);


เพิ่มและลบ Permission ภายในทีม


ในทีมเช่นเดียวกับการให้สิทธิ์และลบของ User แค่เพิ่มพารามิเตอร์ระบุว่าเป็นทีมอะไรเข้าไป

$team = Team::where('name', 'my-awesome-team')->first();
$editUser = Permission::where('name', 'edit-user')->first();
$user->attachPermission([$editUser], $team); //สามารถใส่ได้หลายสิทธิ์โดยการระบุเป็นอาเรย์


การลบ Permission ภายทีม

$user->detachPermission($editUser, $team); //ลบทีละสิทธิ์
$user->detachPermissions([$editUser, $manageUsers], $team); //ลบหลายสิทธิ์ทีเดียว


การซิงค์ Permission ภายในทีม

$user->syncPermissions([$editUser, $manageUsers], $team);


เช็ค Role ในทีม

$user->hasRole('admin', 'my-awesome-team');
$user->hasRole(['admin', 'user'], 'my-awesome-team', true);


เช็ค Permission ในทีม

$user->can('edit-user', 'my-awesome-team');
$user->can(['edit-user', 'manage-users'], 'my-awesome-team', true);


เช็คความสามารถของ user ในทีม

$options = [
    'requireAll' => true | false (Default: false),
    'foreignKeyName'  => 'canBeAnyString' (Default: null)
];
$user->ability(['admin'], ['edit-user'], 'my-awesome-team');
$user->ability(['admin'], ['edit-user'], 'my-awesome-team', $options);


เช็ค Permission, Roles และ ความเป็นเจ้าของในทีม

$options = [
    'team' => 'my-awesome-team',
    'requireAll' => false,
    'foreignKeyName' => 'writer_id'
];
$post = Post::find(1);
$user->canAndOwns(['edit-post', 'delete-post'], $post, $options);
$user->hasRoleAndOwns(['admin', 'writer'], $post, $options);


ใช้งาน Middleware


เราสามารถใช้ middleware ในการกรอง route เดี่ยว และกลุ่มของ route ด้วย permission, roles หรือ ability มาดูตัวอย่างกันเลย

Route::group(['prefix' => 'admin', 'middleware' => ['role:admin']], function() {
    Route::get('/', '[email protected]');
    Route::get('/manage', ['middleware' => ['permission:manage-admins'], 'uses' => '[email protected]']);
});


การใช้ | เพื่อระบุว่าเป็นอย่างใดอย่างหนึ่ง

'middleware' => ['role:admin|root']
// $user->hasRole(['admin', 'root']);
'middleware' => ['permission:edit-post|edit-user']
// $user->hasRole(['edit-post', 'edit-user']);


ต้องการให้ตรงทุกเงื่อนไขโดยเพิ่ม require_all

 'middleware' => ['role:owner|writer,require_all']
// $user->hasRole(['owner', 'writer'], true);
'middleware' => ['permission:edit-post|edit-user,require_all']
// $user->can(['edit-post', 'edit-user'], true);


หรือถ้าต้องการเช็คแบบซับซ้อนให้ใช้ ability โดยมาระบุค่า 3 พารามิเตอร์ได้แก่ roles, permissions และ options

'middleware' => ['ability:admin|owner,create-post|edit-user,require_all']
// $user->ability(['admin', 'owner'], ['create-post', 'edit-user'], true)


ถ้าต้องการให้ middleware เช็คสำหรับภายในทีม 

'middleware' => ['role:admin|root,my-awesome-team,require_all']
// $user->hasRole(['admin', 'root'], 'my-awesome-team', true);
'middleware' => ['permission:edit-post|edit-user,my-awesome-team,require_all']
// $user->hasRole(['edit-post', 'edit-user'], 'my-awesome-team', true);
'middleware' => ['ability:admin|owner,create-post|edit-user,my-awesome-team,require_all']
// $user->ability(['admin', 'owner'], ['create-post', 'edit-user'], 'my-awesome-team', true);


การ return ค่าของ Middleware

เราสามารถตั้งค่าที่ไฟล์ config/laratrust.php ซึ่งจะมีก 2 ประเภทคือ
Abort  คือจะ return เป็น code 403 (ค่าเริ่มต้น)
Redirect ให้ไปที่หน้าอื่น เราสามารถตั้งค่าหน้าที่จะให้ redirect ไปด้วยการตั้งค่า

'middleware_handling' => 'redirect',
'middleware_params'   => '/home',       // เปลี่ยนเป็น route ที่ต้องการได้เลย


Soft Deleting


สำหรับวิธีการที่เราจะลบ Role ออกนั้น แนะนำว่าไม่ควรลบแบบถาวร forceDelete() เลยทันทีนะ เพราะว่า role นั้นหายไปก็จริง แต่ข้อมูล Permission ที่เกี่ยวข้องกับ role นั้น และ user ที่มี role นั้นยังคงอยู่ และไม่เป็นเรื่องดีแน่ถ้ามีการเรียกใช้ข้อมูลจาก permission หรือ user ขึ้นมาแล้วปรากฎว่า role นั้นหายไปแล้ว มันก็จะ error และอีกอย่างข้อมูลขยะใน database ก็จะเยอะด้วย

ดังนั้นควรจะเราแบบปกติก่อน แล้วตามลบข้อมูลที่สัมพันธ์กัน จากนั้นถึงลบถาวรได้ ตามตัวอย่างนี้ครับ

$role = Role::findOrFail(1);

// ลบแบบปกติ
$role->delete();
// ลบทุกข้อมูลที่สัมพันธ์กัน
$role->users()->sync([]);
$role->permissions()->sync([]); // ลบแบบถาวร
$role->forceDelete();


ตัวอย่างการใช้งานใน Blade Templates


การใช้งานจะเหมือนกันเราใช้ @if แต่เขียนในแบบย่อ เช่น 

ถ้าต้องการให้แสดงเฉพาะคนที่มี role ที่กำหนด

@role('admin')
...
@endrole


ถ้าต้องการให้แสดงเฉพาะคนที่มี permission ที่กำหนด

@permission('manage-admins')
...
@endpermission


ถ้าต้องการให้แสดงเฉพาะคนที่มีความสามารถตามเงื่อนไข

@ability('admin,owner', 'create-post,edit-user')
...
@endability


ถ้าต้องการให้แสดงเฉพาะคนที่มี permission ที่กำหนด และเป็นเจ้าของ object นั้นด้วย

@canAndOwns('edit-post', $post)
...
@endOwns


ถ้าต้องการให้แสดงเฉพาะคนที่มี role ที่กำหนด และเป็นเจ้าของ object นั้นด้วย

@hasRoleAndOwns('admin', $post)
...
@endOwns


และนี่ก็คือทั้งหมดของแพคเกจ Laratrust ครับ เป็นบทความที่ยาวที่สุดเคยเขียนมา อาจมีการเรียบเรียงบกพร่องไปบ้าง แต่จะพยายามปรับปรุงให้กระชับอ่านง่ายขึ้นนะครับ หากผิดพลาดก็ขออภัย หากถูกใจก็ขอขอบคุณนะครับ :)


Image



0 Replies


Your Reply