ข้ามไปยังเนื้อหา

ADR-006 - ระบบการอนุญาตโมดูล

ระบบการอนุญาตแบบลำดับชั้นอย่างละเอียดสำหรับโมดูล XOOPS ช่วยให้สามารถควบคุมการเข้าถึงแบบละเอียดได้


ยอมรับแล้ว - นำไปใช้ใน XOOPS 2.5.x และขยายใน XOOPS 4.0


โมดูล XOOPS ต้องการการควบคุมสิทธิ์ที่ยืดหยุ่นที่อนุญาต:

  1. สิทธิ์ระดับโมดูล - ผู้ใช้สามารถเข้าถึงโมดูลนี้ได้หรือไม่
  2. สิทธิ์ระดับออบเจ็กต์ - ผู้ใช้สามารถเข้าถึงรายการเฉพาะนี้ได้หรือไม่
  3. สิทธิ์ระดับการดำเนินการ - ผู้ใช้สามารถดำเนินการนี้ได้หรือไม่
  4. สิทธิ์ที่กำหนดเอง - โมดูลสามารถกำหนดการอนุญาตของตนเองได้หรือไม่

XOOPS 2.5 ใช้ระบบ XoopsGroupPermission:

<?php
$perm_handler = xoops_getHandler('groupperm');
$isAllowed = $perm_handler->checkRight(
'modulename',
'action',
$itemId,
$groupId
);
  1. การสืบค้นที่ซับซ้อน - การตรวจสอบสิทธิ์จำเป็นต้องรวมฐานข้อมูล
  2. ลำดับชั้นที่จำกัด - สร้างกลุ่มสิทธิ์ได้ยาก
  3. การแคชไม่ดี - ไม่มีการแคชสิทธิ์ในตัว
  4. รูปแบบโมดูล - แต่ละโมดูลมีการใช้งานที่แตกต่างกัน
  5. ประสิทธิภาพ - คำขอ DB หลายรายการสำหรับการตรวจสอบสิทธิ์

สร้างระบบอนุญาตแคชที่ได้มาตรฐานซึ่งสนับสนุน:

  1. สิทธิ์แบบลำดับชั้น - การสืบทอดจากกลุ่มหลัก
  2. การเข้าถึงตามบทบาท - แมปการอนุญาตสำหรับบทบาท (ผู้ดูแลระบบ ผู้ดูแล ผู้ใช้ แขก)
  3. การอนุญาตวัตถุ - การควบคุมแบบละเอียดต่อรายการ
  4. แคช - สิทธิ์แคชเพื่อลดการสืบค้น
  5. สิทธิ์ที่กำหนดเอง - โมดูลกำหนดสิทธิ์ของตนเอง
  6. เส้นทางการตรวจสอบ - การเปลี่ยนแปลงการอนุญาตบันทึก
User
└── Group 1 (Admin)
└── Permission: admin_module
└── Permission: edit_all_items
└── Permission: delete_all_items
└── Group 2 (Moderator)
└── Permission: moderate_comments
└── Permission: edit_own_items
└── Group 3 (User)
└── Permission: view_published_items
└── Permission: edit_own_items
└── Group 4 (Guest)
└── Permission: view_published_items
mermaid
graph TB
subgraph "Permission System"
A["Permission Registry<br/>(Define permissions)"]
B["Permission Checker<br/>(Check access)"]
C["Permission Cache<br/>(Improve performance)"]
D["Permission Audit Log<br/>(Track changes)"]
end
subgraph "Data Layer"
E["Group Permissions Table"]
F["User Groups Table"]
G["Permission Definitions"]
end
A --> E
B --> E
B --> C
C --> E
D --> E
F --> B

<?php
// Module defines its permissions in xoops_version.php
$modversion['permissions'] = [
[
'name' => 'module_view',
'description' => 'Can view module',
'level' => 'module',
],
[
'name' => 'item_view',
'description' => 'Can view items',
'level' => 'item',
],
[
'name' => 'item_create',
'description' => 'Can create items',
'level' => 'item',
],
[
'name' => 'item_edit',
'description' => 'Can edit items',
'level' => 'item',
],
[
'name' => 'item_delete',
'description' => 'Can delete items',
'level' => 'item',
],
[
'name' => 'admin_manage',
'description' => 'Can manage module',
'level' => 'admin',
],
];
// Default permissions by group
$modversion['group_permissions'] = [
// Admin group gets all permissions
'1' => [
'module_view' => 1,
'item_view' => 1,
'item_create' => 1,
'item_edit' => 1,
'item_delete' => 1,
'admin_manage' => 1,
],
// User group
'3' => [
'module_view' => 1,
'item_view' => 1,
'item_create' => 1,
'item_edit' => 0,
'item_delete' => 0,
'admin_manage' => 0,
],
// Guest group
'4' => [
'module_view' => 1,
'item_view' => 1,
'item_create' => 0,
'item_edit' => 0,
'item_delete' => 0,
'admin_manage' => 0,
],
];
<?php
declare(strict_types=1);
namespace XoopsCore\Permission;
class PermissionChecker
{
private PermissionCache $cache;
private PermissionRepository $repository;
public function hasPermission(
User $user,
string $permissionName,
?int $itemId = null
): bool {
// Check cache first
$cacheKey = "perm_{$user->getId()}_{$permissionName}_{$itemId}";
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$hasPermission = false;
// Check all user groups
foreach ($user->getGroups() as $group) {
if ($this->checkGroupPermission($group, $permissionName, $itemId)) {
$hasPermission = true;
break;
}
}
// Cache result
$this->cache->set($cacheKey, $hasPermission, 3600);
// Log high-level access checks
if ($hasPermission && $this->shouldAuditLog($permissionName)) {
$this->auditLog('PERMISSION_CHECKED', [
'user_id' => $user->getId(),
'permission' => $permissionName,
'item_id' => $itemId,
'result' => 'ALLOWED',
]);
}
return $hasPermission;
}
private function checkGroupPermission(
Group $group,
string $permissionName,
?int $itemId = null
): bool {
$sql = 'SELECT COUNT(*) FROM ' . $this->table . '
WHERE groupid = ?
AND permission = ?
AND itemid = ?
AND granted = 1';
$stmt = $this->db->prepare($sql);
$stmt->execute([$group->getId(), $permissionName, $itemId ?? 0]);
return $stmt->fetchColumn() > 0;
}
}
<?php
// Different permission levels with different scopes
class PermissionLevel
{
// Module-level: Affects entire module
public const LEVEL_MODULE = 'module';
// Admin-level: Admin panel access
public const LEVEL_ADMIN = 'admin';
// Item-level: Specific objects/items
public const LEVEL_ITEM = 'item';
// Field-level: Specific object fields
public const LEVEL_FIELD = 'field';
// Action-level: Specific actions/operations
public const LEVEL_ACTION = 'action';
}
<?php
// Fine-grained control for specific items
class Item extends XoopsObject
{
/**
* Check if user can view this item
*/
public function canView(User $user): bool
{
// Public items anyone can view
if ($this->getVar('status') === 'published') {
return true;
}
// Owner can always view their items
if ($this->getVar('user_id') === $user->getId()) {
return true;
}
// Check group permissions
$permChecker = xoops_getActiveModule()->getPermissionChecker();
return $permChecker->hasPermission(
$user,
'item_view',
$this->getVar('id')
);
}
public function canEdit(User $user): bool
{
// Owner can edit their items
if ($this->getVar('user_id') === $user->getId()) {
return $permChecker->hasPermission($user, 'item_edit', $this->getVar('id'));
}
// Check if user can edit all items
return $permChecker->hasPermission($user, 'item_edit_all', $this->getVar('id'));
}
public function canDelete(User $user): bool
{
return $permChecker->hasPermission($user, 'item_delete', $this->getVar('id'));
}
}
<?php
// Example: Article controller
class ArticleController
{
private PermissionChecker $permChecker;
public function view(int $id, User $user): Response
{
$article = $this->repository->find($id);
// Check permission
if (!$article->canView($user)) {
throw new AccessDeniedException('Cannot view this article');
}
return new HtmlResponse($this->renderArticle($article));
}
public function edit(int $id, User $user): Response
{
$article = $this->repository->find($id);
// Check permission
if (!$article->canEdit($user)) {
throw new AccessDeniedException('Cannot edit this article');
}
// Handle form submission
if ($this->request->isMethod('POST')) {
$article->setVar('title', $this->request->getPost('title'));
$article->setVar('content', $this->request->getPost('content'));
$this->repository->insert($article);
$this->auditLog('ARTICLE_EDITED', ['id' => $id, 'user_id' => $user->getId()]);
// Invalidate permission cache
$this->permChecker->clearCache($user->getId());
return new RedirectResponse('/article/' . $id);
}
return new HtmlResponse($this->renderForm($article));
}
public function delete(int $id, User $user): Response
{
$article = $this->repository->find($id);
if (!$article->canDelete($user)) {
throw new AccessDeniedException('Cannot delete this article');
}
$this->repository->delete($article);
$this->auditLog('ARTICLE_DELETED', ['id' => $id, 'user_id' => $user->getId()]);
// Invalidate cache
$this->permChecker->clearCache($user->getId());
return new JsonResponse(['success' => true]);
}
}

  1. การควบคุมแบบละเอียด - การจัดการสิทธิ์ที่ได้รับการปรับแต่งอย่างละเอียด
  2. ได้มาตรฐาน - สอดคล้องกันในทุกโมดูล
  3. แคช - ปรับปรุงประสิทธิภาพด้วยการแคช
  4. ตรวจสอบได้ - ติดตามว่าใครเปลี่ยนแปลงอะไร
  5. ยืดหยุ่น - รองรับการอนุญาตแบบกำหนดเอง
  6. ปรับขนาดได้ - จัดการลำดับชั้นการอนุญาตที่ซับซ้อน
  7. ทดสอบได้ - ทดสอบหน่วยได้ง่าย
  1. ความซับซ้อน - มีโค้ดให้จัดการมากขึ้น
  2. ค่าใช้จ่ายฐานข้อมูล - ตารางและการรวมเพิ่มเติม
  3. แคชใช้ไม่ได้ - ต้องล้างแคชเมื่อมีการเปลี่ยนแปลง
  4. Learning Curve - นักพัฒนาต้องเข้าใจระบบ
  5. ประสิทธิภาพ - หากแคชกำหนดค่าไม่ถูกต้อง
ความเสี่ยงความรุนแรงการบรรเทาผลกระทบ
สิทธิ์ที่ซับซ้อนมากเกินไปปานกลางค่าเริ่มต้นที่ดี เอกสารประกอบ
แคชข้อมูลเก่าสูงTTL การทำให้เป็นโมฆะอย่างชาญฉลาด
การถดถอยประสิทธิภาพปานกลางเกณฑ์มาตรฐาน เพิ่มประสิทธิภาพการสืบค้น
บายพาสการอนุญาตสูงการตรวจสอบความปลอดภัย การทดสอบ

<?php
// User can edit their own items but not others'
public function canEdit(User $user): bool
{
// Owner can always edit
if ($this->isOwner($user)) {
return true;
}
// Check group permissions for editing others' items
return $this->permChecker->hasPermission($user, 'edit_all_items');
}
private function isOwner(User $user): bool
{
return $this->getVar('user_id') === $user->getId();
}
<?php
// Different permissions based on status
public function canView(User $user): bool
{
switch ($this->getVar('status')) {
case 'published':
// Anyone with module permission can view
return $this->permChecker->hasPermission($user, 'item_view');
case 'draft':
// Only owner or admin can view
return $this->isOwner($user) ||
$this->permChecker->hasPermission($user, 'admin_manage');
case 'archived':
// Only admin can view
return $this->permChecker->hasPermission($user, 'admin_manage');
default:
return false;
}
}
<?php
// Check against specific roles
public function hasAdminRole(User $user): bool
{
return $user->getGroups()->contains('admin_group');
}
public function hasModeratorRole(User $user): bool
{
return $user->getGroups()->contains('moderator_group') ||
$this->hasAdminRole($user);
}
public function canModerate(User $user): bool
{
return $this->hasModeratorRole($user);
}

  • ADR-001: สถาปัตยกรรมโมดูลาร์ - โมดูลกำหนดสิทธิ์
  • ADR-004: ระบบรักษาความปลอดภัย - รากฐานเพื่อความปลอดภัย
  • ADR-005: มิดเดิลแวร์ - สามารถบังคับใช้สิทธิ์ได้


  • กำหนดระดับการอนุญาตมาตรฐาน
  • สร้างคลาส PermissionChecker
  • ใช้กลยุทธ์การแคช
  • เพิ่มการบันทึกการตรวจสอบ
  • สร้างฟังก์ชันตัวช่วย
  • เขียนแบบทดสอบที่ครอบคลุม
  • เอกสารสำหรับนักพัฒนา
  • อัปเดตโมดูลทั้งหมด
  • การเพิ่มประสิทธิภาพการทำงาน
  • การตรวจสอบความปลอดภัย

เวอร์ชั่นวันที่การเปลี่ยนแปลง
1.0.02024-01-28เอกสารเริ่มต้น

#xoops #adr #permissions #authorization #rbac #security