Fork me on GitHub
#pathom
<
2021-08-30
>
caleb.macdonaldblack00:08:03

Regarding authorization & permissions, almost every resolver that takes input needs to do an permissions check to see if the requesting client/user is permitted to retrieve the resource. Does this makes sense or am I doing something wrong?

caleb.macdonaldblack00:08:58

Perhaps I could whitelist my pathom inputs coming in from the client?

wilkerlucio03:08:27

a good place to isolate this kind of logic is to use a transform function (https://pathom3.wsscode.com/docs/resolvers/#resolver-transform), this looks like this:

(ns com.wsscode.scrapper.auth-demo
  (:require [com.wsscode.pathom3.connect.operation :as pco]
            [com.wsscode.pathom3.connect.indexes :as pci]
            [com.wsscode.pathom3.interface.eql :as p.eql]))

(defn auth-transform [resolver-config]
  (update resolver-config
    ::pco/resolve
    (fn [resolve]
      (fn [{::keys [authorized?] :as env} input]
        (if-not authorized?
          (throw (ex-info "Unauthorized" {})))
       
        (resolve env input)))))

(pco/defresolver user-by-id []
  {::pco/transform auth-transform}
  {:user/name "foo"})

(def env
  (pci/register user-by-id))

(comment
  ; fails
  (p.eql/process-one
    (pci/register user-by-id)
    :user/name)

  ; succeed
  (p.eql/process-one
    (pci/register {::authorized? true} user-by-id)
    :user/name))

wilkerlucio03:08:28

another approach is to write a plugin to wrap all resolver calls, this way you can affect the whole system at once, but I find this approach a bit overkill, and adds overhead to resolvers that could avoid it.

wilkerlucio03:08:54

another way is to categorize any resolver that is fetching restricted data to be a special kind of resolver, in the sense that you made a custom constructor (you can wrap the macro defresolver with your own macro), so you use that macro, and at that level you ensure its setup with the security checks in place

wilkerlucio03:08:39

I believe when you said "take input" you mean identifier inputs, usually :user/id, :group/id, etc... IME these are usually a small part compared to the number of other attributes that might be inputs for transformation, so a nice way to write some generic code for it is to use a consistent heuristic (like: every attribute :*/id is an indentifier and requires checks), or keeping a set with the list of which they are, so you can process then generically

caleb.macdonaldblack23:08:08

@U066U8JQJ Your last comment describes my issue. I thought about processing the client inputs server-side stripping out data that isn’t whitelisted, and then also checking the user has permission to access resources the data identifies. I could get pretty far and save time doing this, however there are use-cases in my permissions model that require me to go further. For example just because one user type can access one resource, doesn’t mean they can access another resources referenced by the first. initial input sanitation won’t cover this scenario and I would need to go further. I’m playing around with the idea that resolvers in my client accessible service-side index cannot trust its inputs and must use “User” data in the env map to return results the user is authorised to retrieve. The caveat being my data access layer needs to use the user object to determine what it can return. Mostly this just looks like table joins to the permissions tables. So far that is working for me but it’s more work than a more generic solution.

stuartrexking04:08:44

Why would params not show up in (-> env :ast)?

wilkerlucio19:08:10

depends at which point the param is, and where you are looking for it (I mean, at which resolver, and how its that related to the param), can you send an reproducible example?

JAtkins21:08:32

P2 or P3? I think it’s been removed in P3, @U066U8JQJ?

wilkerlucio21:08:19

good point, in P3 you should use the (pco/params env) instead of (-> env :ast)

wilkerlucio21:08:10

but I guess the issue may be the more common misunderstanding about params location

JAtkins06:09:08

Where do params appear? I just ran into this misunderstanding myself with p3. When attaching a param to a property, the params are not passed too resolvers required to fulfill that first requirement. e.g.

(defresolver a [env _] (pco/params env) #_=>nil {:a 1})
(defresolver b [env {:keys [a]}] (pco/params env) #_=>{:param :param} {:b (inc a)})

(p.eql/process env ['(:b {:param :param})])
This is on the FAQ page of p3 docs, but currently blank.

wilkerlucio11:09:58

params do not flow to dependencies, the behavior you sent is the expected one

wilkerlucio12:09:45

that FAQ page isn't released, haha, weird it got indexed on search

stuartrexking04:08:08

I have a query with params but they don’t appear in the env :ast