bp/app/Services/AccessService.php

571 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Models\OrganizationUserModel;
use CodeIgniter\Database\Exceptions\DataException;
/**
* AccessService - Сервис проверки прав доступа (RBAC)
*
* Управляет правами внутренних пользователей организации.
* Для внешних пользователей (публичные ссылки) используется ExternalAccessService.
*/
class AccessService
{
private ?array $currentMembership = null;
private OrganizationUserModel $orgUserModel;
/**
* Системные роли (уровень всей системы, не организации)
*/
public const SYSTEM_ROLE_USER = 'user';
public const SYSTEM_ROLE_ADMIN = 'admin';
public const SYSTEM_ROLE_SUPERADMIN = 'superadmin';
/**
* Роли организации (для быстрого сравнения)
*/
public const ROLE_OWNER = 'owner';
public const ROLE_ADMIN = 'admin';
public const ROLE_MANAGER = 'manager';
public const ROLE_GUEST = 'guest';
/**
* Иерархия ролей (больше = выше привилегии)
*/
public const ROLE_HIERARCHY = [
self::ROLE_OWNER => 100,
self::ROLE_ADMIN => 75,
self::ROLE_MANAGER => 50,
self::ROLE_GUEST => 25,
];
/**
* Права на действия
*/
public const PERMISSION_VIEW = 'view';
public const PERMISSION_CREATE = 'create';
public const PERMISSION_EDIT = 'edit';
public const PERMISSION_DELETE = 'delete';
public const PERMISSION_DELETE_ANY = 'delete_any';
public const PERMISSION_MANAGE_USERS = 'manage_users';
public const PERMISSION_MANAGE_MODULES = 'manage_modules';
public const PERMISSION_VIEW_FINANCE = 'view_finance';
public const PERMISSION_DELETE_ORG = 'delete_org';
public const PERMISSION_TRANSFER_OWNER = 'transfer_owner';
/**
* Матрица прав по ролям
* Формат: [роль => [ресурс => [действия]]]
*/
private const ROLE_PERMISSIONS = [
self::ROLE_OWNER => [
'*' => ['*'], // Полный доступ ко всему
],
self::ROLE_ADMIN => [
'clients' => ['view', 'create', 'edit', 'delete'],
'deals' => ['view', 'create', 'edit', 'delete'],
'bookings' => ['view', 'create', 'edit', 'delete'],
'projects' => ['view', 'create', 'edit', 'delete'],
'tasks' => ['view', 'create', 'edit', 'delete'],
'users' => [self::PERMISSION_VIEW, self::PERMISSION_CREATE, self::PERMISSION_EDIT, self::PERMISSION_DELETE],
self::PERMISSION_MANAGE_MODULES => [self::PERMISSION_VIEW, self::PERMISSION_EDIT],
self::PERMISSION_VIEW_FINANCE => ['*'],
],
self::ROLE_MANAGER => [
'clients' => ['view', 'create', 'edit', 'delete'],
'deals' => ['view', 'create', 'edit', 'delete'],
'bookings' => ['view', 'create', 'edit', 'delete'],
'projects' => ['view', 'create', 'edit', 'delete'],
'tasks' => ['view', 'create', 'edit', 'delete'],
'users' => [self::PERMISSION_VIEW], // Только просмотр коллег
],
self::ROLE_GUEST => [
'clients' => [self::PERMISSION_VIEW],
'deals' => [self::PERMISSION_VIEW],
'bookings' => [self::PERMISSION_VIEW],
'projects' => [self::PERMISSION_VIEW],
'tasks' => [self::PERMISSION_VIEW],
'users' => [self::PERMISSION_VIEW],
],
];
private ?string $cachedSystemRole = null;
public function __construct()
{
$this->orgUserModel = new OrganizationUserModel();
}
/**
* Получение единственного экземпляра сервиса
*
* @return self
*/
public static function getInstance(): self
{
return new self();
}
/**
* Получение текущего membership пользователя
*
* @return array|null
*/
public function getCurrentMembership(): ?array
{
if ($this->currentMembership !== null) {
return $this->currentMembership;
}
$userId = session()->get('user_id');
$orgId = session()->get('active_org_id');
if (!$userId || !$orgId) {
return null;
}
$this->currentMembership = $this->orgUserModel
->where('user_id', $userId)
->where('organization_id', $orgId)
->first();
return $this->currentMembership;
}
/**
* Получение роли текущего пользователя
*
* @return string|null
*/
public function getCurrentRole(): ?string
{
$membership = $this->getCurrentMembership();
return $membership['role'] ?? null;
}
/**
* Проверка, авторизован ли пользователь в организации
*
* @return bool
*/
public function isAuthenticated(): bool
{
return $this->getCurrentMembership() !== null;
}
/**
* Проверка роли пользователя
*
* @param string|array $roles Роль или массив ролей для проверки
* @return bool
*/
public function isRole($roles): bool
{
$currentRole = $this->getCurrentRole();
if ($currentRole === null) {
return false;
}
$roles = (array) $roles;
return in_array($currentRole, $roles, true);
}
/**
* Проверка, является ли пользователь владельцем
*
* @return bool
*/
public function isOwner(): bool
{
return $this->getCurrentRole() === self::ROLE_OWNER;
}
/**
* Проверка, является ли пользователем администратором
*
* @return bool
*/
public function isAdmin(): bool
{
$role = $this->getCurrentRole();
return $role === self::ROLE_ADMIN || $role === self::ROLE_OWNER;
}
/**
* Проверка, является ли пользователем менеджером или выше
*
* @return bool
*/
public function isManagerOrHigher(): bool
{
$role = $this->getCurrentRole();
return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_MANAGER], true);
}
// =========================================================================
// СИСТЕМНЫЕ РОЛИ (суперадмин)
// =========================================================================
/**
* Получение системной роли пользователя
*
* @return string|null
*/
public function getSystemRole(): ?string
{
if ($this->cachedSystemRole !== null) {
return $this->cachedSystemRole;
}
$userId = session()->get('user_id');
if (!$userId) {
return null;
}
$userModel = new \App\Models\UserModel();
$user = $userModel->find($userId);
$this->cachedSystemRole = $user['system_role'] ?? null;
return $this->cachedSystemRole;
}
/**
* Проверка системной роли пользователя
*
* @param string|array $roles Роль или массив ролей для проверки
* @return bool
*/
public function isSystemRole($roles): bool
{
$currentRole = $this->getSystemRole();
if ($currentRole === null) {
return false;
}
$roles = (array) $roles;
return in_array($currentRole, $roles, true);
}
/**
* Проверка, является ли пользователь суперадмином
*
* @return bool
*/
public function isSuperadmin(): bool
{
return $this->getSystemRole() === self::SYSTEM_ROLE_SUPERADMIN;
}
/**
* Проверка, является ли пользователь системным администратором
*
* @return bool
*/
public function isSystemAdmin(): bool
{
$role = $this->getSystemRole();
return in_array($role, [self::SYSTEM_ROLE_ADMIN, self::SYSTEM_ROLE_SUPERADMIN], true);
}
/**
* Сброс кэша системной роли
*
* @return void
*/
public function resetSystemRoleCache(): void
{
$this->cachedSystemRole = null;
}
/**
* Проверка права на действие
*
* @param string $action Действие (view, create, edit, delete, delete_any, manage_users, etc.)
* @param string $resource Ресурс (clients, deals, bookings, projects, tasks, users)
* @return bool
*/
public function can(string $action, string $resource): bool
{
$role = $this->getCurrentRole();
if ($role === null) {
return false;
}
$permissions = self::ROLE_PERMISSIONS[$role] ?? [];
// Проверка полного доступа (*)
if (isset($permissions['*']) && in_array('*', $permissions['*'], true)) {
return true;
}
// Проверка конкретного ресурса
if (!isset($permissions[$resource])) {
return false;
}
$resourcePermissions = $permissions[$resource];
// Проверка полного доступа к ресурсу
if (in_array('*', $resourcePermissions, true)) {
return true;
}
return in_array($action, $resourcePermissions, true);
}
/**
* Проверка права на просмотр
*
* @param string $resource
* @return bool
*/
public function canView(string $resource): bool
{
return $this->can(self::PERMISSION_VIEW, $resource);
}
/**
* Проверка права на создание
*
* @param string $resource
* @return bool
*/
public function canCreate(string $resource): bool
{
return $this->can(self::PERMISSION_CREATE, $resource);
}
/**
* Проверка права на редактирование
*
* @param string $resource
* @return bool
*/
public function canEdit(string $resource): bool
{
return $this->can(self::PERMISSION_EDIT, $resource);
}
/**
* Проверка права на удаление
*
* @param string $resource
* @param bool $any Удаление любой записи (не только своей)
* @return bool
*/
public function canDelete(string $resource, bool $any = false): bool
{
$action = $any ? self::PERMISSION_DELETE_ANY : self::PERMISSION_DELETE;
return $this->can($action, $resource);
}
/**
* Проверка права на управление пользователями
*
* Управление пользователями недоступно для личных пространств.
*
* @return bool
*/
public function canManageUsers(): bool
{
// Проверяем, что это бизнес-организация (не личное пространство)
$orgId = session()->get('active_org_id');
if (!$orgId) {
return false;
}
$orgModel = new \App\Models\OrganizationModel();
$organization = $orgModel->find($orgId);
// Личное пространство - управление пользователями недоступно
if ($organization && $organization['type'] === 'personal') {
return false;
}
return $this->can(self::PERMISSION_MANAGE_USERS, 'users');
}
/**
* Проверка права на управление модулями
*
* @return bool
*/
public function canManageModules(): bool
{
return $this->can(self::PERMISSION_MANAGE_MODULES, self::PERMISSION_MANAGE_MODULES);
}
/**
* Проверка права на просмотр финансов
*
* @return bool
*/
public function canViewFinance(): bool
{
return $this->can(self::PERMISSION_VIEW_FINANCE, self::PERMISSION_VIEW_FINANCE);
}
/**
* Проверка права на удаление организации
*
* @return bool
*/
public function canDeleteOrganization(): bool
{
return $this->isOwner();
}
/**
* Проверка права на передачу прав владельца
*
* @return bool
*/
public function canTransferOwnership(): bool
{
return $this->isOwner();
}
/**
* Получение роли для передачи прав владельца (список допустимых ролей)
*
* @return array
*/
public function getRolesEligibleForOwnershipTransfer(): array
{
// Только admin может стать новым владельцем
return [self::ROLE_ADMIN];
}
/**
* Получение уровня роли (для сравнения)
*
* @param string $role
* @return int
*/
public function getRoleLevel(string $role): int
{
return self::ROLE_HIERARCHY[$role] ?? 0;
}
/**
* Проверка, имеет ли роль достаточно прав для сравнения
*
* @param string $role
* @param string $requiredRole
* @return bool
*/
public function hasRoleLevel(string $role, string $requiredRole): bool
{
return $this->getRoleLevel($role) >= $this->getRoleLevel($requiredRole);
}
/**
* Получение списка доступных ролей для назначения
*
* @param string $assignerRole Роль назначающего
* @return array
*/
public function getAvailableRolesForAssignment(string $assignerRole): array
{
$allRoles = [self::ROLE_ADMIN, self::ROLE_MANAGER, self::ROLE_GUEST];
// Owner может назначать любые роли
if ($assignerRole === self::ROLE_OWNER) {
return $allRoles;
}
// Admin не может назначать owner
if ($assignerRole === self::ROLE_ADMIN) {
return [self::ROLE_ADMIN, self::ROLE_MANAGER, self::ROLE_GUEST];
}
// Manager и Guest не могут назначать роли
return [];
}
/**
* Сброс кэша membership (при переключении организации)
*
* @return void
*/
public function resetCache(): void
{
$this->currentMembership = null;
$this->cachedSystemRole = null;
}
/**
* Получение текстового названия роли
*
* @param string $role
* @return string
*/
public function getRoleLabel(string $role): string
{
$labels = [
self::ROLE_OWNER => 'Владелец',
self::ROLE_ADMIN => 'Администратор',
self::ROLE_MANAGER => 'Менеджер',
self::ROLE_GUEST => 'Гость',
];
return $labels[$role] ?? $role;
}
/**
* Получение всех ролей с описаниями
*
* @return array
*/
public static function getAllRoles(): array
{
return [
self::ROLE_OWNER => [
'label' => 'Владелец',
'description' => 'Полный доступ к организации',
'level' => 100,
],
self::ROLE_ADMIN => [
'label' => 'Администратор',
'description' => 'Управление пользователями и модулями',
'level' => 75,
],
self::ROLE_MANAGER => [
'label' => 'Менеджер',
'description' => 'Полный доступ к функционалу модулей',
'level' => 50,
],
self::ROLE_GUEST => [
'label' => 'Гость',
'description' => 'Только просмотр данных',
'level' => 25,
],
];
}
/**
* Получение всех системных ролей с описаниями
*
* @return array
*/
public static function getAllSystemRoles(): array
{
return [
self::SYSTEM_ROLE_USER => [
'label' => 'Пользователь',
'description' => 'Обычный пользователь системы',
],
self::SYSTEM_ROLE_ADMIN => [
'label' => 'Администратор',
'description' => 'Администратор системы',
],
self::SYSTEM_ROLE_SUPERADMIN => [
'label' => 'Суперадмин',
'description' => 'Полный доступ ко всем функциям системы',
],
];
}
}