1
0
Files
TaskBoard/backend/app/class/enity/class_projectAccess.php
Falknat 190b4d0a5e Большое обновление
1. Создание личных проектов
2. Управление командой
3. Приглашение участников
4. Уведомления

и многое другое...
2026-01-18 20:17:02 +07:00

541 lines
24 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
class ProjectAccess {
// Список всех доступных прав
const PERMISSIONS = [
'create_task', // создание задач
'edit_task', // редактирование любых задач
'edit_own_task_only', // редактирование только назначенных на себя
'delete_task', // удаление задач
'move_task', // перемещение любых задач между колонками
'move_own_task_only', // перемещение только назначенных на себя
'archive_task', // архивирование задач
'create_column', // создание колонок
'edit_column', // редактирование колонок
'delete_column', // удаление колонок
'manage_departments', // управление отделами
'remove_members', // удаление участников (приглашать/редактировать могут только админы)
'create_comment', // создание комментариев в любых задачах
'create_comment_own_task_only', // создание комментариев только в назначенных на себя
'delete_own_comments', // удаление своих комментариев
'delete_all_comments', // удаление любых комментариев в проекте
'upload_files', // загрузка файлов
'upload_images' // загрузка картинок
];
// Права по умолчанию для нового участника
const DEFAULT_PERMISSIONS = [
'create_task' => true, // создание задач
'edit_task' => false, // редактирование любых задач
'edit_own_task_only' => true, // редактирование только назначенных на себя
'delete_task' => false, // удаление задач
'move_task' => false, // перемещение любых задач между колонками
'move_own_task_only' => true, // перемещение только назначенных на себя
'archive_task' => true, // архивирование задач
'create_column' => false, // создание колонок
'edit_column' => false, // редактирование колонок
'delete_column' => false, // удаление колонок
'manage_departments' => false, // управление отделами
'remove_members' => false, // удаление участников
'create_comment' => false, // создание комментариев в любых задачах
'create_comment_own_task_only' => true, // создание комментариев только в назначенных на себя
'delete_own_comments' => true, // удаление своих комментариев
'delete_all_comments' => false, // удаление любых комментариев
'upload_files' => true, // загрузка файлов
'upload_images' => true // загрузка картинок
];
// ==================== ПРОВЕРКИ ДОСТУПА ====================
// Проверить, является ли пользователь владельцем проекта (в id_admin таблицы projects)
public static function isOwner(int $project_id, int $user_id): bool {
$project = Database::get('project', ['id_admin'], ['id' => $project_id]);
if (!$project || !$project['id_admin']) {
return false;
}
$owners = json_decode($project['id_admin'], true);
return is_array($owners) && in_array($user_id, $owners);
}
// Проверить, является ли пользователь участником проекта
public static function isMember(int $project_id, int $user_id): bool {
// Владелец всегда имеет доступ
if (self::isOwner($project_id, $user_id)) {
return true;
}
return Database::has('project_members', [
'id_project' => $project_id,
'id_user' => $user_id
]);
}
// Проверить, является ли пользователь админом проекта
public static function isAdmin(int $project_id, int $user_id): bool {
// Владелец = безоговорочный админ
if (self::isOwner($project_id, $user_id)) {
return true;
}
return Database::has('project_members', [
'id_project' => $project_id,
'id_user' => $user_id,
'is_admin' => 1
]);
}
// Проверить конкретное право (владелец/админ имеет все права)
public static function can(int $project_id, int $user_id, string $permission): bool {
// Владелец может всё
if (self::isOwner($project_id, $user_id)) {
return true;
}
$member = Database::get('project_members', ['is_admin', 'permissions'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return false;
}
// Админ может всё
if ((int)$member['is_admin'] === 1) {
return true;
}
$permissions = $member['permissions'] ? json_decode($member['permissions'], true) : [];
return isset($permissions[$permission]) && $permissions[$permission] === true;
}
// Проверить право на редактирование задачи (с учётом edit_own_task_only)
// "Своя задача" = назначен на пользователя ИЛИ создана пользователем
public static function canEditTask(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
if (self::isAdmin($project_id, $user_id)) {
return true;
}
if (self::can($project_id, $user_id, 'edit_task')) {
return true;
}
// Создатель + право создания → может редактировать свои созданные
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
return true;
}
// Назначена на себя + право edit_own_task_only
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'edit_own_task_only')) {
return true;
}
return false;
}
// Проверить право на перемещение задачи (с учётом move_own_task_only)
// "Своя задача" = назначен на пользователя ИЛИ создана пользователем
public static function canMoveTask(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
if (self::isAdmin($project_id, $user_id)) {
return true;
}
if (self::can($project_id, $user_id, 'move_task')) {
return true;
}
// Создатель + право создания → может перемещать свои созданные
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
return true;
}
// Назначена на себя + право move_own_task_only
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'move_own_task_only')) {
return true;
}
return false;
}
// Проверить право на создание комментария в задаче
public static function canCreateComment(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
if (self::isAdmin($project_id, $user_id)) {
return true;
}
// Полное право — комментировать любые задачи
if (self::can($project_id, $user_id, 'create_comment')) {
return true;
}
// Создатель + право создания → может комментировать свои созданные
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
return true;
}
// Назначена на себя + право create_comment_own_task_only
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'create_comment_own_task_only')) {
return true;
}
return false;
}
// ==================== ПРОВЕРКИ С EXIT ====================
// Требовать доступ к проекту (при отказе — 403 и exit)
public static function requireAccess(int $project_id, ?int $user_id): void {
if (!$user_id) {
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
}
if (!self::isMember($project_id, $user_id)) {
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет доступа к проекту']], 403);
}
}
// Требовать права админа
public static function requireAdmin(int $project_id, ?int $user_id): void {
if (!$user_id) {
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
}
if (!self::isAdmin($project_id, $user_id)) {
RestApi::response(['success' => false, 'errors' => ['access' => 'Требуются права администратора']], 403);
}
}
// Требовать конкретное право
public static function requirePermission(int $project_id, ?int $user_id, string $permission): void {
if (!$user_id) {
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
}
if (!self::can($project_id, $user_id, $permission)) {
RestApi::response(['success' => false, 'errors' => ['access' => 'Недостаточно прав']], 403);
}
}
// Требовать право на редактирование задачи
public static function requireEditTask(int $project_id, ?int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): void {
if (!$user_id) {
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
}
if (!self::canEditTask($project_id, $user_id, $task_assignee_id, $task_creator_id)) {
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет прав на редактирование задачи']], 403);
}
}
// Требовать право на перемещение задачи
public static function requireMoveTask(int $project_id, ?int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): void {
if (!$user_id) {
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
}
if (!self::canMoveTask($project_id, $user_id, $task_assignee_id, $task_creator_id)) {
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет прав на перемещение задачи']], 403);
}
}
// ==================== УПРАВЛЕНИЕ УЧАСТНИКАМИ ====================
// Добавить участника в проект
public static function addMember(int $project_id, int $user_id, int $current_user_id, bool $is_admin = false, ?array $permissions = null): array {
// Нельзя пригласить себя
if ($user_id === $current_user_id) {
return ['success' => false, 'errors' => ['member' => 'Нельзя пригласить себя в проект']];
}
if (self::isMember($project_id, $user_id)) {
return ['success' => false, 'errors' => ['member' => 'Пользователь уже участник проекта']];
}
$perms = $permissions ?? self::DEFAULT_PERMISSIONS;
Database::insert('project_members', [
'id_project' => $project_id,
'id_user' => $user_id,
'is_admin' => $is_admin ? 1 : 0,
'permissions' => json_encode($perms),
'created_at' => date('Y-m-d H:i:s')
]);
return ['success' => true, 'id' => Database::id()];
}
// Удалить участника из проекта (владельца удалить нельзя, админ не может удалить себя)
public static function removeMember(int $project_id, int $user_id, int $current_user_id): array {
// Владельца удалить нельзя
if (self::isOwner($project_id, $user_id)) {
return ['success' => false, 'errors' => ['member' => 'Нельзя удалить владельца проекта']];
}
$member = Database::get('project_members', ['is_admin'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
}
// Админ может выйти сам, если есть другие админы или владелец
// (владелец всегда есть в id_admin проекта, так что это безопасно)
Database::delete('project_members', [
'id_project' => $project_id,
'id_user' => $user_id
]);
return ['success' => true];
}
// Получить всех участников проекта (включая владельцев из id_admin)
public static function getMembers(int $project_id): array {
$result = [];
$addedUserIds = [];
// Получаем владельцев из id_admin проекта
$project = Database::get('project', ['id_admin'], ['id' => $project_id]);
if ($project && $project['id_admin']) {
$ownerIds = json_decode($project['id_admin'], true);
if (is_array($ownerIds) && count($ownerIds) > 0) {
$owners = Database::select('accounts', ['id', 'name', 'username', 'avatar_url', 'telegram'], ['id' => $ownerIds]);
foreach ($owners as $owner) {
$result[] = [
'id' => null,
'id_user' => (int)$owner['id'],
'is_admin' => true,
'is_owner' => true,
'permissions' => [],
'created_at' => null,
'name' => $owner['name'],
'username' => $owner['username'],
'avatar_url' => $owner['avatar_url'],
'telegram' => $owner['telegram']
];
$addedUserIds[] = (int)$owner['id'];
}
}
}
// Получаем участников из project_members
$members = Database::select('project_members', [
'[>]accounts' => ['id_user' => 'id']
], [
'project_members.id',
'project_members.id_user',
'project_members.is_admin',
'project_members.permissions',
'project_members.created_at',
'accounts.name',
'accounts.username',
'accounts.avatar_url',
'accounts.telegram'
], [
'project_members.id_project' => $project_id
]);
foreach ($members as $member) {
$userId = (int)$member['id_user'];
// Пропускаем если уже добавлен как владелец
if (in_array($userId, $addedUserIds)) {
continue;
}
$result[] = [
'id' => $member['id'],
'id_user' => $userId,
'is_admin' => (int)$member['is_admin'] === 1,
'is_owner' => false,
'permissions' => $member['permissions'] ? json_decode($member['permissions'], true) : [],
'created_at' => $member['created_at'],
'name' => $member['name'],
'username' => $member['username'],
'avatar_url' => $member['avatar_url'],
'telegram' => $member['telegram']
];
}
return $result;
}
// Получить ID всех проектов пользователя (включая где он владелец)
public static function getUserProjectIds(int $user_id): array {
$projectIds = [];
// Проекты из project_members
$rows = Database::select('project_members', ['id_project'], [
'id_user' => $user_id
]);
foreach ($rows as $row) {
$projectIds[] = (int)$row['id_project'];
}
// Проекты где пользователь в id_admin
$allProjects = Database::select('project', ['id', 'id_admin']);
foreach ($allProjects as $project) {
if ($project['id_admin']) {
$owners = json_decode($project['id_admin'], true);
if (is_array($owners) && in_array($user_id, $owners)) {
$pid = (int)$project['id'];
if (!in_array($pid, $projectIds)) {
$projectIds[] = $pid;
}
}
}
}
return $projectIds;
}
// ==================== УПРАВЛЕНИЕ ПРАВАМИ ====================
// Установить конкретное право
public static function setPermission(int $project_id, int $user_id, string $permission, bool $value, int $current_user_id): array {
// Нельзя редактировать свои права
if ($user_id === $current_user_id) {
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
}
if (!in_array($permission, self::PERMISSIONS)) {
return ['success' => false, 'errors' => ['permission' => 'Неизвестное право: ' . $permission]];
}
$member = Database::get('project_members', ['permissions'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
}
$permissions = $member['permissions'] ? json_decode($member['permissions'], true) : [];
$permissions[$permission] = $value;
Database::update('project_members', [
'permissions' => json_encode($permissions)
], [
'id_project' => $project_id,
'id_user' => $user_id
]);
return ['success' => true, 'permissions' => $permissions];
}
// Установить несколько прав сразу
public static function setPermissions(int $project_id, int $user_id, array $permissions, int $current_user_id): array {
// Нельзя редактировать свои права
if ($user_id === $current_user_id) {
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
}
// Нельзя редактировать права владельца
if (self::isOwner($project_id, $user_id)) {
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять права владельца проекта']];
}
foreach (array_keys($permissions) as $perm) {
if (!in_array($perm, self::PERMISSIONS)) {
return ['success' => false, 'errors' => ['permission' => 'Неизвестное право: ' . $perm]];
}
}
$member = Database::get('project_members', ['permissions'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
}
$currentPerms = $member['permissions'] ? json_decode($member['permissions'], true) : [];
$mergedPerms = array_merge($currentPerms, $permissions);
Database::update('project_members', [
'permissions' => json_encode($mergedPerms)
], [
'id_project' => $project_id,
'id_user' => $user_id
]);
return ['success' => true, 'permissions' => $mergedPerms];
}
// Получить права участника
public static function getPermissions(int $project_id, int $user_id): ?array {
// Владелец имеет все права
if (self::isOwner($project_id, $user_id)) {
$allPerms = [];
foreach (self::PERMISSIONS as $perm) {
$allPerms[$perm] = true;
}
return $allPerms;
}
$member = Database::get('project_members', ['is_admin', 'permissions'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return null;
}
// Админ имеет все права
if ((int)$member['is_admin'] === 1) {
$allPerms = [];
foreach (self::PERMISSIONS as $perm) {
$allPerms[$perm] = true;
}
return $allPerms;
}
return $member['permissions'] ? json_decode($member['permissions'], true) : [];
}
// Назначить/снять админа
public static function setAdmin(int $project_id, int $user_id, bool $is_admin, int $current_user_id): array {
// Нельзя редактировать свои права
if ($user_id === $current_user_id) {
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
}
// Нельзя изменять статус владельца
if (self::isOwner($project_id, $user_id)) {
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять статус владельца проекта']];
}
$member = Database::get('project_members', ['id'], [
'id_project' => $project_id,
'id_user' => $user_id
]);
if (!$member) {
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
}
Database::update('project_members', [
'is_admin' => $is_admin ? 1 : 0
], [
'id_project' => $project_id,
'id_user' => $user_id
]);
return ['success' => true];
}
// Получить список всех доступных прав (для UI)
public static function getAvailablePermissions(): array {
return self::PERMISSIONS;
}
// Получить дефолтные права (для UI)
public static function getDefaultPermissions(): array {
return self::DEFAULT_PERMISSIONS;
}
}
?>