권한 프리디케이트를 제안할 적절한 곳은 어디인가요?

Restrict exposure of full name to certain groups 의 해결책을 제안하기 위해, 다음과 같은 질문을 답변하는 (서버 측) 권한 예제(predicate)를 정의하고 싶습니다.

현재 사용자 범위/컨텍스트에 따라 (다른) 사용자의 전체 이름을 공개해도 되는가?

(범위는 클라이언트 요청이나 다이제스트 이메일 메시지 생성과 같은 내부 처리 단계에 의해 설정될 수 있습니다.)

이러한 예제를 위한 올바른 구조는 무엇일까요? 코드베이스의 어디에 위치해야 할까요? 아마도 이는 가디언(guardian)에 속해야 할 것 같은데, 제 생각이 맞나요? 어느 가디언에 넣어야 하는지에 대한 의견이 있으신가요?

이 새로운 예제는 현재 코드베이스에 존재하는 SiteSettings.enable_names?의 많은(하지만 전부는 아닌) 인스턴스를 포함하게 될 것으로 예상됩니다. (user.name을 반환하는 직렬화기(Serializer)를 수정하면 대부분의 데이터 누출을 막을 수 있지만, 전부는 아닙니다.) 이 방식의 좋은 점은, 향후 플러그인이 동작을 변경할 수 있는 확장 지점을 도입한다는 것입니다. (제 지식 범위 내에서는 현재 이것이 불가능하지만, 가능하다면 그냥 그렇게 했을 것입니다.) 따라서 이 작업을 성공적으로 완료하면, 이 변경사항은 Discourse 코어에 대한 풀 리퀘스트가 될 것입니다 — 그리고 저는 수락될 가능성이 있는 PR을 제출하고 싶습니다.

내가 파악/추론한 내용은 다음과 같습니다:

  • Guardian은 _“사용자가 무엇을 할 수 있는가?”_를 캡슐화하는 것이 맞습니다. (Guardian 인스턴스에는 User 인스턴스도 포함됩니다.)
  • 따라서 권한 판정식(predicate)을 두는 적절한 위치는 단순히 Guardian의 메서드(lib/guardian.rb)입니다.
    • 해당 메서드가 "사용자가 특정 Xxxx 객체에 대해 Z 작업을 수행할 수 있는가?"를 묻는 것이라면, XxxxGuardian 믹스인 파일(lib/guardian/...) 중 하나에 속할 가능성이 높습니다.
    • 그렇지 않다면, 기본 Guardian 정의에 넣어야 합니다.
  • ApplicationController는 현재 요청/클라이언트를 반영하는 guardian 속성을 관리하며, 이를 시리얼라이저(serializer)의 scope로 제공합니다. 덕분에 필요할 때 현재 Guardian을 사용할 수 있습니다([1]가 아닌 경우를 제외하고).
  • 시스템이 실행하는 백엔드 태스크와 같이 미리 만들어진 Guardian이 사용 불가능한 경우가 있습니다. 하지만 “행동하는 사용자(acting user)”(예: 이메일 알림을 생성할 때의 수신자)에 대한 참조를 가지고 있다면, 즉석에서 적절한 가디언을 생성할 수 있습니다: Guardian.new(the_user).

  1. ↩︎