Где лучше предложить предикат разрешений?

В целях предложения решения для Restrict exposure of full name to certain groups, я хотел бы определить предикат прав доступа (на стороне сервера), который отвечает на вопрос:

Исходя из текущего контекста/области видимости пользователя, допустимо ли раскрывать полные имена (других) пользователей?

(Область видимости может задаваться клиентским запросом или на каком-то этапе внутренней обработки, например, при генерации дайджест-писем.)

Какая конструкция подходит для такого предиката? Где в кодеbase ему следует находиться? Мне кажется, что это может быть реализовано в guardian — правильно ли я мыслю? Есть ли какие-либо мнения о том, какой именно guardian выбрать?

Я предполагаю, что этот новый предикат охватит многие (но не все) случаи использования SiteSettings.enable_names?, которые сейчас есть в кодеbase. (Обращение к сериализаторам, возвращающим user.name, закрывает большинство утечек данных, но не все.) Приятным преимуществом этого подхода является то, что он создаст точку расширения, через которую будущий плагин сможет изменить поведение (насколько мне известно, сейчас это невозможно, иначе я бы уже это сделал!). Таким образом, если мне удастся это реализовать, я отправлю pull-request в ядро Discourse — и я хочу представить PR, который имеет шансы на принятие.

Вот что я понял/вывел:

  • Guardian действительно отвечает за инкапсуляцию вопроса «Что пользователю разрешено делать?» (экземпляр Guardian также содержит экземпляр User).
  • Следовательно, правильное место для предиката прав доступа — это просто метод в классе Guardian (lib/guardian.rb).
    • Если метод отвечает на вопрос «Может ли пользователь сделать Z с объектом типа Xxxx?», то он, скорее всего, должен находиться в одном из файлов миксинов XxxxGuardian (lib/guardian/...).
    • В противном случае он добавляется в базовое определение Guardian.
  • ApplicationController управляет атрибутом guardian, отражающим текущий запрос/клиент, и передаёт его сериализаторам в качестве их scope, так что текущий Guardian доступен при необходимости (за исключением случаев, когда это невозможно[1]).
  • Существуют места, где готовый экземпляр Guardian недоступен, обычно это фоновые задачи, выполняемые системой. Однако если у вас есть доступ к «действующему пользователю» (например, пользователю-получателю при генерации уведомления по электронной почте), вы можете создать соответствующий Guardian на лету: Guardian.new(the_user).

  1. ↩︎