Аутентификация в политиках и сценариях

Аутентификация в политиках и сценариях

Привязка профиля аутентификации закрывает пути: целиком, все кроме перечисленных или только перечисленные. Если правило зависит от чего-то ещё — метода запроса, заголовка, адреса клиента или от того, кто вошёл, — используйте политики и сценарии.

Что можно сделать

  • Потребовать вход по условию — действие Аутентификация в политике или вызов authenticate() в сценарии.
  • Проверить, кто вошёл — условие Аутентификация в политике или identity() в сценарии.
  • Передать приложению сведения о пользователе в своём заголовке — в сценарии.

Порядок проверки

Сначала срабатывают привязки профилей к сервису, затем политики и сценарии. Политика может ужесточить требование привязки, но не отменить его: путь, закрытый привязкой, политикой не открыть.

Условия Аутентификация сами вход не требуют. Они видят только пользователя, которого уже узнала привязка или действие Аутентификация, выполненное раньше. Если клиент не входил, условие ложно, а с отметкой НЕ — истинно.

Политики

Действие «Аутентификация»

Действие — Аутентификация, Атрибут — Профилем, Параметры — нужный профиль. Действие доступно в месте помещения Обработка запроса на пути к серверу.

Клиент, который ещё не вошёл, получает требование входа этого профиля: окно браузера, форму входа или ответ 401 — в зависимости от модулей приёма профиля.

Пример: вход только на разделе /basic.

ГруппаАтрибутЗначение
ЕСЛИHTTP URIСегмент пути =/basic
ТОАутентификацияПрофилемBASIC AUTH

Условие «Аутентификация»

АтрибутИстинно, когда
Пользовательимя пользователя равно значению
Группапользователь состоит в группе
Атрибутатрибут пользователя с указанным именем равен значению

Если на запросе сработали несколько профилей, группы и атрибуты объединяются, а имя пользователя берётся у последнего сработавшего профиля.

Пример: раздел /admin только для группы admin.

НЕГруппаАтрибутЗначение
ЕСЛИ✔АутентификацияГруппа =admin
ИHTTP URIСегмент пути =/admin
ТОКастомная страницаПростой HTMLстраница «Нет доступа»

Раздел /admin должен быть закрыт привязкой профиля к сервису. Иначе клиент, который ещё не вошёл, сразу получит страницу «Нет доступа» вместо предложения войти.

Сценарии

Потребовать вход

authenticate("Имя профиля", { параметры })

Профиль указывают по имени или по UUID. Ссылку на него добавьте в раздел Связанные сущности с типом Профиль аутентификации: без неё вызов завершится ошибкой. UUID надёжнее имени — переименование профиля ломает вызовы, которые ссылались на него по имени.

Все параметры необязательны. Пути и группы можно задать строкой или списком.

ПараметрЧто делает
onlyтребовать вход только на этих путях
exceptтребовать вход везде, кроме этих путей
groupsпускать только пользователей хотя бы из одной группы
on_denyответ при отказе по группам: { status = 403 }, { redirect = "/denied" } или { body = "<h1>Нет доступа</h1>" }
app_logout_pathsадреса выхода приложения

only и except вместе указать нельзя. Если не задан ни один из них, вход требуется на каждом запросе, который дошёл до вызова. Неизвестный параметр — ошибка сценария: опечатка вроде group вместо groups не пропустит молча всех.

Узнать, кто вошёл

local auth = require("auth")
local me = auth.identity()   -- nil или { user = "...", groups = { ... }, attrs = { ... } }
 
if auth.in_group(me, "admins") then
    -- ...
end

Пример: разные клиенты — разные профили

Интеграции приходят с токеном от IdP, люди и скрипты — с логином и паролем. Читать может любой вошедший, изменять — только администраторы. /api/health открыт для мониторинга.

local method = ngx.req.get_method()
local writes = method ~= "GET" and method ~= "HEAD"
local authorization = ngx.var.http_authorization or ""
 
if authorization:find("^Bearer ") then
    authenticate("JWT BACKEND TEST", {
        except = "/api/health",
        groups = writes and { "admins" } or nil,  -- группа из токена IdP
    })
else
    authenticate("BASIC AUTH", {
        except = "/api/health",
        groups = writes and { "admin" } or nil,   -- группа из набора пользователей
    })
end
 
-- Приложению не нужно разбирать токен: кто пришёл, видно в отдельном заголовке.
local me = require("auth").identity()
if me then
    set_req_header("X-Caller", me.user)
else
    -- На /api/health входа нет: не даём клиенту подставить X-Caller самому.
    ngx.req.clear_header("X-Caller")
end