Вы когда-нибудь давали менеджеру доступ к CRM, а он случайно удалил заказы другого менеджера? Или клиент увидел чужие заявки из-за ошибки в URL? Это называется «проблема разграничения прав доступа», и она может стоить вам клиентов, денег и нервов.
В Falcon Space разграничение прав строится на трёх уровнях: доступ к страницам (роли), доступ к данным (фильтрация в SQL-процедурах) и доступ к действиям (кнопки). Это гибкая система, которая позволяет настроить права так, что каждый пользователь будет видеть ровно то, что ему положено. Расскажу, как это работает на практике.
В платформе нет жёстких предопределённых ролей. Вы сами создаёте роли под свой бизнес: «Клиент», «Менеджер», «Директор», «Бухгалтер», «Супервайзер» — любые. Каждая роль — это просто имя и набор разрешений.
Разграничение строится так:
Рассмотрим каждый уровень подробно.
В Falcon Space каждая страница имеет поле «Роли». Вы указываете через запятую, какие роли могут видеть эту страницу. Например: Роли: Admin, Manager. Если пользователь не входит ни в одну из этих ролей, при попытке открыть страницу он получит ошибку 403 (доступ запрещён).
Это самый простой и эффективный способ скрыть от лишних глаз целые разделы. Например:
Настройка делается через административный интерфейс: выбираете страницу, редактируете поле «Роли», сохраняете. Всё, никакого кода.
Это самый тонкий момент. У вас есть таблица заказов, и разные пользователи должны видеть разные заказы. Клиент — только свои. Менеджер — заказы своего отдела. Директор — все.
В Falcon Space это реализуется через передачу параметра @username в хранимые процедуры. Система автоматически подставляет имя текущего пользователя (логин) во все вызовы процедур. Внутри процедуры вы пишете условие, например:
CREATE PROCEDURE [app].[get_orders]
@username nvarchar(100)
AS
BEGIN
-- Для клиента: только его заказы
IF EXISTS (SELECT 1 FROM users WHERE username = @username AND role = 'Client')
SELECT * FROM orders WHERE client_username = @username;
-- Для менеджера: заказы его подразделения
ELSE IF EXISTS (SELECT 1 FROM users WHERE username = @username AND role = 'Manager')
SELECT o.* FROM orders o
JOIN departments d ON o.department_id = d.department_id
JOIN users u ON u.department_id = d.department_id
WHERE u.username = @username;
-- Для директора: все заказы
ELSE IF EXISTS (SELECT 1 FROM users WHERE username = @username AND role = 'Director')
SELECT * FROM orders;
ELSE
SELECT NULL WHERE 1=0; -- ничего не возвращаем
END
Важно: никогда не доверяйте параметрам, переданным с клиента. Например, не делайте так: WHERE order_id = @order_id_from_request без проверки, принадлежит ли этот заказ пользователю. Злоумышленник может подставить чужой order_id и получить чужие данные. Всегда используйте @username как идентификатор текущего пользователя.
Такой подход гарантирует, что даже если пользователь угадает URL или передаст чужой ID, он не увидит чужие данные — процедура сама отфильтрует.
Часто нужно, чтобы начальник видел данные подчинённых. Добавляем в таблицу пользователей поле manager_id. В процедуре делаем рекурсию (через CTE) или просто выбираем всех, у кого менеджер = текущий пользователь.
WITH subordinates AS (
SELECT user_id FROM users WHERE manager_username = @username
UNION ALL
SELECT u.user_id FROM users u
JOIN subordinates s ON u.manager_username = (SELECT username FROM users WHERE user_id = s.user_id)
)
SELECT * FROM orders WHERE client_username IN (SELECT username FROM users WHERE user_id IN (SELECT user_id FROM subordinates));
Так можно строить сколь угодно глубокие иерархии.
Даже если пользователь видит заказ, он не обязательно должен иметь возможность его редактировать или удалять. В Falcon Space каждая кнопка, ссылка, форма привязаны к определённой процедуре. Вы можете в процедуре сделать проверку роли и, если пользователь не имеет права, вернуть ошибку или просто не выполнять действие.
Пример: процедура удаления заказа должна быть доступна только менеджеру и директору, но не клиенту.
CREATE PROCEDURE [app].[delete_order]
@order_id int,
@username nvarchar(100)
AS
BEGIN
-- Проверяем роль
DECLARE @role nvarchar(50);
SELECT @role = role FROM users WHERE username = @username;
IF @role NOT IN ('Manager', 'Director')
BEGIN
SELECT 'error' AS result, 'У вас нет прав на удаление заказов' AS message;
RETURN;
END
-- Дополнительная проверка: может, заказ уже оплачен? Тогда удалять нельзя.
IF EXISTS (SELECT 1 FROM orders WHERE order_id = @order_id AND status = 'Paid')
BEGIN
SELECT 'error' AS result, 'Нельзя удалить оплаченный заказ' AS message;
RETURN;
END
DELETE FROM orders WHERE order_id = @order_id;
SELECT 'success' AS result;
END
На странице кнопка «Удалить» будет видна всем, но при нажатии вызовется процедура и отклонит запрос, если у пользователя нет прав. Альтернативно — можно скрыть кнопку по роли прямо в HTML, но проверка на сервере обязательна (на случай, если пользователь отправит запрос в обход интерфейса).
Иногда нужно, чтобы разные роли видели разные столбцы в одной таблице. Например, менеджер видит цену закупки, а клиент — нет. В процедуре можно динамически формировать SELECT:
IF @role = 'Manager'
SELECT order_id, client_name, total_sum, purchase_price, profit;
ELSE
SELECT order_id, client_name, total_sum;
Или возвращать одно и то же, но на фронтенде скрывать колонки через CSS (менее безопасно).
В кейсе с автосервисом (см. предыдущие статьи) мы использовали роли:
Для каждой роли мы создали отдельную процедуру вывода данных, либо общую с условиями. Это заняло несколько дней проектирования, но зато система работает без сбоев уже год, и ни один пользователь не смог увидеть чужие данные.
Из отзыва: «Гибкое разделение видимости данных в зависимости от должности сотрудника и его места работы. Множество аналитических отчётов. Фиксация действий: изменение полей, сохранение форм, посещение страниц».
Права доступа бесполезны, если вы не знаете, что происходит. Рекомендуем включить логирование всех критических действий: создание/изменение/удаление заказов, изменение цен, вход в систему, выгрузка отчётов. В Falcon Space это делается через триггеры в SQL или через вызов процедуры логирования из каждой бизнес-процедуры.
Пример триггера на таблицу заказов:
CREATE TRIGGER trg_orders_audit ON orders
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
INSERT INTO audit_log (table_name, action, username, changed_data, changed_at)
SELECT 'orders',
CASE WHEN EXISTS (SELECT 1 FROM inserted) AND EXISTS (SELECT 1 FROM deleted) THEN 'UPDATE'
WHEN EXISTS (SELECT 1 FROM inserted) THEN 'INSERT'
ELSE 'DELETE' END,
SYSTEM_USER,
(SELECT * FROM inserted FOR JSON PATH),
GETDATE();
END
Теперь при любом изменении заказа вы знаете, кто и когда это сделал. Это дисциплинирует сотрудников и помогает расследовать инциденты.
Всё это можно сделать без программирования, кроме шага 4 (написание условий в SQL). Но SQL-условия — это база, которую может освоить даже не программист за пару дней.
Проведите аудит: какие страницы, какие процедуры. Начните с малого — добавьте проверку роли на странице «Мои заказы» для клиентов. Затем распространите на другие. Часто достаточно просто добавить условие WHERE client_username = @username в существующие процедуры, и проблема решена.
Мы можем помочь вам с аудитом и доработкой прав. Стоимость — от 20 до 100 тыс. руб в зависимости от объёма. Зато вы будете спать спокойно.
Итог: разграничение прав в Falcon Space — это комбинация ролей на страницах, фильтрации по @username в SQL-процедурах и проверки роли в действиях. При правильной настройке вы получите систему, где каждый пользователь видит только своё, а вы контролируете каждый шаг. Не пренебрегайте безопасностью.