Files
bp/app/Services/AccessService.php

571 lines
17 KiB
PHP

<?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' => 'Полный доступ ко всем функциям системы',
],
];
}
}