Přeskočit na obsah

ADR-006 - Systém povolení modulu

ADR-006: Systém povolení modulu

Sekce “ADR-006: Systém povolení modulu”

Jemně strukturovaný hierarchický systém oprávnění pro moduly XOOPS umožňující granulární řízení přístupu.


Přijato – Implementováno v XOOPS 2.5.xa rozšířeno v XOOPS 4.0


Prohlášení o problému

Sekce “Prohlášení o problému”

Moduly XOOPS potřebují flexibilní ovládací prvky oprávnění, které umožňují:

  1. Oprávnění na úrovni modulu – Má uživatel přístup k tomuto modulu?
  2. Oprávnění na úrovni objektu – Má uživatel přístup k této konkrétní položce?
  3. Oprávnění na úrovni akce – Může uživatel provést tuto akci?
  4. Vlastní oprávnění – Mohou moduly definovat svá vlastní oprávnění?

XOOPS 2.5 používá systém XOOPSGroupPermission:

<?php
$perm_handler = xoops_getHandler('groupperm');
$isAllowed = $perm_handler->checkRight(
'modulename',
'action',
$itemId,
$groupId
);
  1. Složité dotazy – Kontroly oprávnění vyžadují připojení k databázi
  2. Limited Hierarchy – Obtížné vytváření skupin oprávnění
  3. Špatné ukládání do mezipaměti – Žádné vestavěné ukládání oprávnění do mezipaměti
  4. Varianty modulu – Každý modul se implementuje jinak
  5. Výkon – Více DB dotazů pro kontrolu oprávnění

Implementujte hierarchický systém povolení

Sekce “Implementujte hierarchický systém povolení”

Vytvořte standardizovaný systém oprávnění uložený v mezipaměti podporující:

  1. Hierarchická oprávnění – Dědičnost z nadřazených skupin
  2. Přístup založený na rolích – Mapování oprávnění k rolím (admin, moderátor, uživatel, host)
  3. Oprávnění k objektu – Jemná kontrola na položku
  4. Ukládání do mezipaměti – Oprávnění ukládat do mezipaměti pro snížení počtu dotazů
  5. Vlastní oprávnění – Moduly definují svá vlastní
  6. Audit Trail – Zaznamenávat změny oprávnění
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
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

1. Definice oprávnění

Sekce “1. Definice oprávnění”
<?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,
],
];

2. Kontrola oprávnění

Sekce “2. Kontrola oprávnění”
<?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;
}
}

3. Úrovně oprávnění

Sekce “3. Úrovně oprávnění”
<?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';
}

4. Oprávnění na úrovni objektů

Sekce “4. Oprávnění na úrovni objektů”
<?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'));
}
}

5. Použití v ovladačích

Sekce “5. Použití v ovladačích”
<?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. Granular Control – Vyladěná správa oprávnění
  2. Standardizované – Konzistentní napříč moduly
  3. Cached – Vylepšený výkon díky ukládání do mezipaměti
  4. Auditable – Sledujte, kdo co změnil
  5. Flexibilní – Podpora vlastních oprávnění
  6. Scalable – Zvládá složité hierarchie oprávnění
  7. Testovatelný – Snadné testování jednotek
  1. Složitost – Více kódu ke správě
  2. Režie databáze – Více stolů a spojení
  3. Neplatnost mezipaměti – Při změnách je nutné vymazat mezipaměť
  4. Křivka učení – Vývojáři musí rozumět systému
  5. Výkon – Pokud mezipaměť není správně nakonfigurována
RizikoZávažnostZmírnění
Příliš složitá oprávněníStředníDobré výchozí hodnoty, dokumentace
Ukládat do mezipaměti zastaralá dataVysokáTTL, inteligentní zneplatnění
Regrese výkonuStředníBenchmark, optimalizace dotazů
Obchvat povoleníVysokáBezpečnostní audit, testy

Vzory návrhu oprávnění

Sekce “Vzory návrhu oprávnění”

Vzor 1: Oprávnění na základě vlastníka

Sekce “Vzor 1: Oprávnění na základě vlastníka”
<?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();
}

Vzor 2: Oprávnění založená na stavu

Sekce “Vzor 2: Oprávnění založená na stavu”
<?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;
}
}

Vzor 3: Oprávnění založená na rolích

Sekce “Vzor 3: Oprávnění založená na rolích”
<?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);
}

Související rozhodnutí

Sekce “Související rozhodnutí”
  • ADR-001: Modulární architektura - Moduly definují oprávnění
  • ADR-004: Bezpečnostní systém – základ pro bezpečnost
  • ADR-005: Middleware - Může vynutit oprávnění


Kontrolní seznam implementace

Sekce “Kontrolní seznam implementace”
  • Definujte standardní úrovně oprávnění
  • Vytvořit třídu PermissionChecker
  • Implementujte strategii ukládání do mezipaměti
  • Přidat protokolování auditu
  • Vytvořte pomocné funkce
  • Napište komplexní testy
  • Dokument pro vývojáře
  • Aktualizovat všechny moduly
  • Optimalizace výkonu
  • Bezpečnostní revize

VerzeDatumZměny
1.0.02024-01-28Počáteční dokument

#xoops #adr #oprávnění #autorizace #rbac #security