{@thread.name}

Jan Ulbrich
2023-06-03

Jan Ulbrich:

Hi there, 👋

I have a query like

{
  alarm(filter: {id: { eq: 4711}}) {
    message
    events(sort: {field: ID, order: DESC}) {
      id
      message
    }
  }
}

which I need to filter with a Ash.Query.after_action .

The filter is called as expected, but the actor of the request (available in the outer filter with the correct value) is nil for this nested.

Do I manually need to forward the context in some way or might that be a bug?

This is the stack trace raising an exception from Ash.Query.after_action (no error, but manually raised to get the stack):

(ash_graphql 0.25.5) lib/graphql/resolver.ex:1806: AshGraphql.Graphql.Resolver.to_resolution/3
(absinthe 1.7.1) lib/absinthe/middleware/dataloader.ex:37: Absinthe.Middleware.Dataloader.get_result/2
(absinthe 1.7.1) lib/absinthe/phase/document/execution/resolution.ex:232: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
(absinthe 1.7.1) lib/absinthe/phase/document/execution/resolution.ex:187: Absinthe.Phase.Document.Execution.Resolution.do_resolve_field/3
...
(phoenix 1.6.16) lib/phoenix/router/route.ex:41: Phoenix.Router.Route.call/2
(phoenix 1.6.16) lib/phoenix/router.ex:354: Phoenix.Router.__call__/2

Jan Ulbrich:

These are the Ash-related dependencies I’m using:

{:ash, "~> 2.9.19"},
{:ash_authentication, "~> 3.11.3"},
{:ash_graphql, "~> 0.25.5"},
{:ash_json_api, git: "https://github.com/ash-project/ash_json_api.git", build: "main"},
{:ash_postgres, "~> 1.3.28"},

zachdaniel:

Can I see what your after action hook looks like?

Jan Ulbrich:

Sorry, I missed your question earlier! Here you go:

  actions do
    read :read do
      primary?(true)

      prepare(fn query, context ->
        IO.inspect(context, label: "Context") # <- actor nil
        Ash.Query.after_action(query, fn _query, events ->
          {:ok, patch_results_applying_actor_filters(events, context)}
        end)
      end)
    end

In that function:

  def patch_results_applying_actor_filters(events, context) do
    with actor when actor != nil <- Map.get(context, :actor) do
      ...
    end
  end

zachdaniel:

can you add a change to the root resource and see if the actor is being passed at all?

zachdaniel:

I don’t see a reason why the actor wouldn’t be available in that context you showed

Jan Ulbrich:

Yes, I’ll add the same debug statement. Gimme a minute.

Jan Ulbrich:

It’s weird: The inner query is kind of executed first

[info] POST /graphql
[debug] Processing with Absinthe.Plug
  Parameters: %{"query" => "{\n  alarm(filter: { id: {eq: 4711}}) {\n    originatedAt\n    extid\n    longitude\n    latitude\n    day\n    month\n    year\n    payload\n    kind\n    subkind\n    events(sort: {field: ORIGINATED_AT}) {\n      originatedAt\n      payload\n    }\n  }\n}", "variables" => nil}
  Pipelines: [:graphql]
[debug] ABSINTHE schema=TickerWeb.Schema variables=%{}
---
{
  alarm(filter: { id: {eq: 4711}}) {
    originatedAt
    extid
    longitude
    latitude
    day
    month
    year
    payload
    kind
    subkind
    events(sort: {field: ORIGINATED_AT}) {
      originatedAt
      payload
    }
  }
}
---
[debug] QUERY OK source="alarms" db=9.9ms queue=0.1ms idle=1777.5ms
SELECT a0."id", a0."created_at", a0."updated_at", a0."year", a0."month", a0."day", a0."originated_at", a0."message", a0."extid", a0."operations_center", a0."kind", a0."subkind", a0."longitude", a0."latitude", a0."payload" FROM "alarms" AS a0 WHERE (a0."id" = $1) [4711]
Inner context: %{tracer: nil, actor: nil, authorize?: nil}
Outer context: %{
  tracer: nil,
  actor: %{
    id: "bwb-102290",
    [...]
  },
  authorize?: true
}
[debug] QUERY OK source="alarms" db=3.7ms idle=1788.8ms
SELECT a0."created_at", a0."updated_at", a0."message", a0."operations_center", a0."id" FROM "alarms" AS a0 WHERE (a0."id"::bigint = $1::bigint) [4711]
[debug] QUERY OK source="events" db=201.7ms idle=1793.6ms
SELECT e0."id", e0."originated_at", e0."extid", e0."payload" FROM "events" AS e0 WHERE (e0."id"::bigint = $1::bigint) ORDER BY e0."originated_at" [4711]

Jan Ulbrich:

I thought, that I may have added some query as a precondition, but that’s not the case: If I remove the relation in the query, there’s no query to the events table at all.

[debug] Processing with Absinthe.Plug
  Parameters: %{"query" => "{\n  alarm(filter: { id: {eq: 4711}}) {\n    originatedAt\n    extid\n    longitude\n    latitude\n    day\n    month\n    year\n    payload\n    kind\n    subkind\n  }\n}", "variables" => nil}
  Pipelines: [:graphql]
[debug] ABSINTHE schema=TickerWeb.Schema variables=%{}
---
{
  alarm(filter: { id: {eq: 4711}}) {
    originatedAt
    extid
    longitude
    latitude
    day
    month
    year
    payload
    kind
    subkind
  }
}
---
[debug] QUERY OK source="alarms" db=9.0ms idle=117.1ms
SELECT a0."id", a0."created_at", a0."updated_at", a0."year", a0."month", a0."day", a0."originated_at", a0."message", a0."extid", a0."operations_center", a0."kind", a0."subkind", a0."longitude", a0."latitude", a0."payload" FROM "alarms" AS a0 WHERE (a0."id" = $1) [4811]

zachdaniel:

it has to do with the after action hooks

zachdaniel:

i.e the inner query’s “after action” happens before the outer query’s “after action”

zachdaniel:

and you’re on the latest ash_graphql?

zachdaniel:

I figured it out

Jan Ulbrich:

Yes, it’s 0.25.5 and installed as that version and not directly from master.

zachdaniel:

releasing a fix now. I was a bit worried as this sounded like something that could affect authorization

zachdaniel:

like the builtin policy authorization

zachdaniel:

but its not really, only if you have query preparations that reference the actor on read

zachdaniel:

I guess in some designs it could have been a security issue

zachdaniel:

but not for people using the policy authorizer at least

zachdaniel:

basically the case is when ash_graphql loaded related things, query preparations would get actor: nil

zachdaniel:

but its highly unlikely that that would cause things to be less secure

zachdaniel:

more like more secure

zachdaniel:

(well, accidentally secure, preventing users who should be able to do things from doing them)

Jan Ulbrich:

Which is at least conservative and wouldn’t compromise date.

zachdaniel:

Yeah

zachdaniel:

anyway 0.25.6 is out w/ the fix 🙂

Jan Ulbrich:

Wow, you’re wild! 😀

Jan Ulbrich:

Yes, that works: I can confirm that I now get the same context and my filters show the right data. Pew… 😌 As always: Thanks again for your fast help! 🚀

zachdaniel:

My pleasure 🙂