В прошлой статье я рассмотрел способ подключения ведущих. Однако, там есть один фатальный недостаток — он использует один общий пароль. Это очень небезопасно: во-первых, если кто-то нашалит, вычислить его будет затруднительно, а во-вторых, уход ведущего может быть сопровождён сменой пароля и повторной его раздачей всем остальным. Как-то неудобно, не правда ли? Liquidsoap решает и эту проблему, позволяя назначить для harbor собственный аутентификатор. Аутентификатор — это функция, возвращающая true или false в зависимости от успешности прохождения. Результат используется «мылом» для того, чтобы решить, пускать юзера в эфир или нет.

В основном для аутентификации используют внешние скрипты. Эти скрипты Liquidsoap вызывает через shell и получает от них строку. Скрипт может делать запросы к БД, искать соответствия в файлах, да что угодно. Сам скрипт аутентификации рассматривать не будем, однако договоримся, что в случае успешной аутентификации скрипт вернёт слово «true», иначе «false». Теперь зададим функцию для проверки авторизации:

# переменная для текущего ведущего. запоминаем, это в качестве бонуса
def current_user = ref "" end # глобальные переменные в Liquidsoap - это жесть

def authorize (user,password) =
  ret = get_process_lines("/usr/local/bin/auth #{shell_escape(password)}")
  ret = list.hd(ret)
  if ret == "true" then # если скрипт вернул то что нужно...
        cu = get_process_output("/usr/local/bin/getusername #{shell_escape(password)}") # получаем юзернейм, зачем - расскажу ниже
        current_user :=  cu # меняем глобальную переменную
    true # возвращаем мылу true
  else
    false # отбрасываем юзера
  end
end

Скрипт использует только поле password. Это сделано потому, что некоторые клиенты для Icecast умеют посылать только пароль вещания, а имя пользователя вшито в их код и состоит из слова source. Используем только пароль.

Обратите внимание: переменные подставляются не просто так — они обрамлены функцией shell_escape. Пароль — данные от пользователя, так что нужно относиться к нему очень недоверчиво и осторожно, очищая его от спецсимволов, тем более, что мы выполняем команду оболочки, используя данные пользователя. Функцию shell_escape ищите в моей статье о регистрации в каталогах.

Теперь изменяем наш harbor, убирая оттуда параметр password и добавляя новый — auth:

harbor = input.harbor("stream", port=1488, auth=authorize)

Теперь харбор будет авторизовать пользователей по специальным парольным фразам, которые генерируются и раздаются каждому ведущему персонально.

 

Бонус: именование файлов записи

В статье об организации эфира я рассказывал, как можно записывать эфиры ведущих и складывать их в файл. Однако, теперь у нас много ведущих, и было бы сусно, если бы по имени файла можно было понять, кто и когда вёл эфир. Именно для этого мы и объявляли глобальную переменную current_user. Предположим, что у нас есть ещё один скрипт, который по парольной фразе возвращает имя пользователя. Обращение к нему описано чуть выше. output.file имеет одну callback-функцию, которая называется on_close и вызывается при закрытии файла. Там-то мы и переименуем наш файл в соответствии с юзернеймом ведущего:

# Функция для вызова при on_close
def file_closed (str) =
        # Составляем новое имя файла, заменяя ~uname~ на текущее имя пользователя
        newn = string.replace(pattern='~uname~', fun(_)->!current_user, str)
        # Переименовываем файл
        system ("mv #{str} #{newn}")
end

output.file(%mp3(bitrate=128),
        # Добавляем ~uname~ - шаблон для подстановки имени пользователя
        "/path/to/archive/live_~uname~_%d_%m_%Y-%H_%M_%S.mp3",
        reopen_on_metadata=false,
        fallible=true,
        # При закрытии - переименовать
        on_close=file_closed,
harbor)

Вот и всё, ребята! :3