Настройка передачи пользовательского контекста в СУБД

Описание

В Alpha BI версии 5.11.0 в настройках соединения для СУБД PostgreSQL, Oracle Database, ClickHouse и продуктов линейки Arenadata добавлены параметры для передачи команд в СУБД:

  • Команда при открытии сессии;
  • Команда при закрытии сессии.

Данная опция позволяет выполнять запросы к БД в контексте текущего пользователя. В частности, решение может применяться для построчного разграничения доступа (RLS) к таблице на стороне БД. Таким образом, обеспечивается применение глобальных политик безопасности без дополнительной настройки правил доступа на уровне приложения.

Пример настройки

Приведённые ниже примеры носят демонстрационный характер и не являются эталонной методикой настройки RLS.
Задача Alpha BI — предоставить возможность выполнять запросы к БД в контексте текущего пользователя, передавая, логин пользователя при открытии сессии. Конкретный способ использования этого контекста на стороне БД — RLS-политики, secure view, словари, роли и т.п. — администратор базы данных выбирает самостоятельно с учётом политики безопасности организации, версии СУБД и принятых практик.

В качестве примера настроим подключение для пользователей с логинами:

  • user_1 — Пользователь с урезанным доступом к данным таблицы;

  • admin — Пользователь с полным доступом к данным таблицы.

Используется таблица countries_rls с данными о странах. Логика доступа: admin видит все строки, user_1 — только разрешённые.

PostgreSQL:

Настройка соединения в Alpha BI :

-- Команда при открытии сессии:*
SET ROLE '${CurrentUser.Login}';


-- Команда при закрытии сессии:* ``
RESET ROLE;

Настройка в БД:

-- Роли-контексты (без LOGIN: в них переключаются через SET ROLE)
CREATE ROLE user_1;
CREATE ROLE admin;

-- Членство технического пользователя в этих ролях — обязательное условие
-- для работы SET ROLE. Замените alphabi_connector на вашу учётку подключения.
GRANT user_1 TO alphabi_connector;
GRANT admin  TO alphabi_connector;

-- Включаем RLS и запрещаем обход владельцу таблицы
ALTER TABLE countries_rls ENABLE ROW LEVEL SECURITY;
ALTER TABLE countries_rls FORCE  ROW LEVEL SECURITY;

-- Даём ролям право читать таблицу
GRANT SELECT ON TABLE countries_rls TO user_1, admin;

-- Политика для user_1: только строки с Id 100 и 101
CREATE POLICY policy_user1_select ON countries_rls
    FOR SELECT
    TO user_1
    USING ("Id" IN (100, 101));

-- Политика для admin: все строки
CREATE POLICY policy_admin_select ON countries_rls
    FOR SELECT
    TO admin
    USING (true);

На что обратить внимание:

  • Владелец таблицы обходит RLS — поэтому нужен FORCE ROW LEVEL SECURITY, либо подключение не владельцем.

  • SET ROLE без членства в роли завершится ошибкой — гранты обязательны.

  • RESET ROLE при закрытии важен для пула соединений, но при аварийном разрыве может не выполниться. Поэтому контекст всегда ставится заново при открытии.

  • Поведение по умолчанию — fail-closed: если для пользователя нет подходящей политики, он не видит ни одной строки.

ClickHouse:

Настройка соединения в Alpha BI:

-- Команда при открытии сессии:
SET log_comment = '${CurrentUser.Login}';

-- Команда при закрытии сессии:
SET log_comment = '';

Настройка в БД :

-- 1. Таблица маппинга: какой пользователь какие локации может видеть
CREATE TABLE user_location_access
(
    user_login String,
    location   String
)
ENGINE = MergeTree()
ORDER BY user_login;

INSERT INTO user_location_access VALUES
    ('user_1', 'Европа');

-- 2. Политика для пользователя с ограниченным доступом
--    Показывает только строки, локация которых разрешена для текущего пользователя.
CREATE ROW POLICY policy_user_select ON countries_rls_user
    FOR SELECT
    USING locations IN (
        SELECT location
        FROM user_location_access
        WHERE user_login = getSetting('log_comment')
    )
    TO ALL;

-- 3. Политика для администратора: видит все строки
CREATE ROW POLICY policy_admin_select ON countries_rls_user
    FOR SELECT
    USING getSetting('log_comment') = 'admin'
    TO ALL;

Альтернатива

Если нужно, чтобы при отсутствии контекста (пустом log_comment) пользователь видел все строки — например, для отладочных или административных запросов — можно использовать одну политику с IF:

CREATE ROW POLICY policy_select ON countries_rls_user
    FOR SELECT
    USING IF(getSetting('log_comment') = '', 1,
        locations IN (
            SELECT location
            FROM user_location_access
            WHERE user_login = getSetting('log_comment')
        )
    )
    TO ALL;

Результат настройки

  • Пользователь с логином admin видит все записи реестра

  • Пользователь с логином user_1 видит записи удовлетворяющие условию locations = 'Европа'

На что обратить внимание:

  • Row policies работают только для read-only пользователей. Если учетная запись подключения Alpha BI может изменять таблицу (или является её владельцем), политика обходится. У пользователя должно быть только GRANT SELECT.

  • Permissive-политики объединяются через OR . Строка видна, если её разрешает хотя бы одна политика. Это позволяет удобно комбинировать правила «для всех» и «для админа».

  • log_comment — это встроенная настройка, которая в первую очередь служит для логирования и наблюдаемости. Она позволяет помечать запросы в system.query_log и в серверных логах. Хотя технически установить её через SET можно, механизм row policies не рассчитывает на чтение произвольных встроенных настроек через getSetting() для целей безопасности. Для передачи пользовательского контекста в ClickHouse (и особенно для RLS) идиоматически правильным и надёжным способом являются “кастомные” настройки сессии.

  • Поведение по умолчанию — fail-closed : если политика не сработала, строк не видно.

Oracle:

-- Команда при открытии сессии:
BEGIN DBMS_SESSION.SET_IDENTIFIER('${CurrentUser.Login}'); END;

-- Команда при закрытии сессии:
BEGIN DBMS_SESSION.CLEAR_IDENTIFIER; END;

Настройка в БД:

-- 1. Таблица маппинга: какой пользователь какие локации может видеть
CREATE TABLE user_location_access (
    user_login VARCHAR2(100) PRIMARY KEY,
    location   VARCHAR2(100)
);

INSERT INTO user_location_access VALUES ('user_1', 'Азия');

-- 2. Функция проверки доступа: используется в secure view
CREATE OR REPLACE FUNCTION check_location_access (
    p_location IN VARCHAR2
) RETURN NUMBER
AS
    v_client_id VARCHAR2(100);
    v_count     NUMBER;
BEGIN
    v_client_id := SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER');

    -- Нет контекста => показываем всё (fail-open)
    IF v_client_id IS NULL THEN
        RETURN 1;
    END IF;

    -- Администратор видит всё
    IF v_client_id = 'admin' THEN
        RETURN 1;
    END IF;

    -- Для остальных — проверка по таблице доступа
    SELECT COUNT(*)
    INTO v_count
    FROM user_location_access
    WHERE user_login = v_client_id
      AND location = p_location;

    RETURN v_count;
END;

-- 3. Secure view: применяет функцию проверки доступа
CREATE OR REPLACE VIEW countries_rls_user_view AS
SELECT *
FROM countries_rls_user
WHERE check_location_access(locations) = 1;

-- 4. Грант техническому пользователю Alpha BI на чтение view
GRANT SELECT ON countries_rls_user_view TO alphabi_connector;

Результат настройки

  • Пользователь с логином admin видит все записи реестра

  • Пользователь с логином user_1 видит записи удовлетворяющие условию locations = 'Азия'

На что обратить внимание

  • CLIENT_IDENTIFIER— это метка сессии, а не аутентификация. Он доступен любому пользователю через DBMS_SESSION.SET_IDENTIFIER, поэтому механизм подходит для разграничения доступа, но не заменяет аутентификацию.

  • Поведение по умолчанию — fail-open : при отсутствии контекста видны все строки. Для fail-closed замените RETURN 1 на RETURN 0 в ветке v_client_id IS NULL.

  • Secure view используется вместо нативного VPD (DBMS_RLS) — это более простой и наглядный вариант для демонстрации. Для полноценного RLS на всех операциях (включая DML) стоит рассмотреть DBMS_RLS.

  • View создаётся в схеме владельца таблицы — техническому пользователю Alpha BI нужен явный грант на чтение view.

Итог

  • Безопасность становится ближе к данным — правила доступа описываются на стороне БД и применяются централизованно.
  • Меньше дублирования — не нужно настраивать логику доступа в приложении для каждой таблицы.
  • Единые правила — одни и те же политики работают для всех запросов из Alpha BI, независимо от источника.