Proper place to propose a permissions predicate?

In the interest of proposing a solution for Restrict exposure of full name to certain groups, I would like to define a (server-side) permssions predicate that answers the question

Based on the current user-scope/context, is it ok to reveal (other) users’ full names?

(The scope could be set by a client request, or by some internal processing step, like generating digest email messages.)

What is the right construct for such a predicate? Where in the codebase should it live? I’m thinking that perhaps this belongs in a guardian — am I on the right track? Any opinions on which one?

I imagine this new predicate would subsume many (but not all) instances of SiteSettings.enable_names? currently in the codebase. (Hitting the Serializers that yield user.name plugs most of the data leaks, but not quite all of them.) A nice benefit of this is that it would introduce an extension point by which a future plugin could change the behavior (not currently possible, as far as I know, or I would just do that!). So, if I get this working, this would become a pull-request to the discourse core — and I would like to submit a PR that has a hope of getting accepted.

1 Like

Here is what I have learned/deduced:

  • Guardian is indeed the thing that encapsulates “What is the user allowed to do?” (A Guardian instance has-a User instance, too.)
  • Thus, the proper place for a permissions predicate is simply as a method on Guardian (lib/guardian.rb).
    • If the method is a “Can the user do Z to an Xxxx object?” then it probably belongs in one of the XxxxGuardian mixin files (lib/guardian/...).
    • Otherwise, it goes into the base Guardian definition.
  • ApplicationController manages a guardian attribute reflecting the current request/client, and provides it to serializers as their scope, so the current Guardian is available when needed (except when it isn’t[1])
  • There are places where a ready-made Guardian is not available, typically in a backend task run by the system, but if you have a handle on an “acting user” (e.g., the recipient user, when generating an email notification), you can create a appropriate guardian on the fly: Guardian.new(the_user) .

  1. ↩︎