Order of @auth directive Processing

If I have nested ands/ors in a @auth directive. Are they processed first to last until a condition is met?

In other words, should my simpler queries be listed first in the array?

Example Schema:

type Post @auth(
  query: {or: [ # either is published w/(public or authenticated user) or author or admin
    {and: [ # is published and either public or authenticated user
      {rule: "query ($NOW: DateTime!) { queryPost(filter: {published: {ge: $NOW}}) { id } }"} # publish date >= now
      {or: [ # either public or authenticated user
        {rule: "query { queryPost(filter: {isPublic: true}) { id } }"}, # allow anyone to see public
        {rule: "$USERROLE: {eq: \"USER\" }"}, # allow users
      ]}
    ]}
    {rule: "query ($USERID: String!) { queryPost { author(filter: {id: [$USERID] }) { id } } }"} # allow authors to see their own
    {rule: "$USERROLE: {eq: \"ADMIN\" }"} # allow admin to see all
  ]}
) {
  id: ID!
  isPublic: Boolean! @search
  published: DateTime! @search
  author: Person
  content: String
}

I understand that in most logic processing a response is returned as soon as possible without needing to continue through the logic if not necessary. I am assuming that Dgraph works the same way.

For performance, I understand that:

  • A variable check against a string value > a variable check against a query
  • A filter against a id > a filter against a Boolean
  • A filter against a Boolean > a filter against a DateTime
  • A filter against a DateTime > a filter against a String
  • A query w/filter on top level > a query w/filter on lower levels

So I think the following would allow for better performance:

type Post @auth(
  query: {or: [ # either admin or author or is published w/(public or authenticated user)
    {rule: "$USERROLE: {eq: \"ADMIN\" }"} # allow admin to see all
    {rule: "query ($USERID: String!) { queryPost { author(filter: {id: [$USERID] }) { id } } }"} # allow authors to see their own
    {and: [ # is published and either public or authenticated user
      {or: [ # either public or authenticated user
        {rule: "$USERROLE: {eq: \"USER\" }"}, # allow users
        {rule: "query { queryPost(filter: {isPublic: true}) { id } }"}, # allow anyone to see public
      ]}
      {rule: "query ($NOW: DateTime!) { queryPost(filter: {published: {ge: $NOW}}) { id } }"} # publish date >= now
    ]}
  ]}
) {
  id: ID!
  isPublic: Boolean! @search
  published: DateTime! @search
  author: Person
  content: String
}

Questions:

  1. Is my understanding of performance above accurate?
  2. Is this accurate that ordering of @auth nested directives matters and is read FIFO?
  3. Is there any way to use replace $NOW in the schema with the current DateTime instead of passing it through a JWT? (maybe should be its own topic)

Hi,

Thanks for such a detailed question.

There’s no sense of FIFO ordering because for example ‘and’ rules find subsets of nodes you can see, so we still have to process all the rules.

Here’s how the rules are processed.

  • we process all the RBAC style rules first (the ones that look like this "$USERROLE: {eq: \"ADMIN\" }" if from that we can evaluate the auth, then we stop there and never go to the DB. For example, in your rule you have or: [ A, B, {rule: "$USERROLE: {eq: \"ADMIN\" }"} if the JWT says they are the admin, we can stop without evaluating anything else. Similarly, for the nested cases, we can cut out evaluation of other rules.
  • after we have evaluated all the RBAC rules, we then run any remaining database rules in parallel.

So the ordering doesn’t matter.

For, your second question, we are looking at adding some ‘default’ values and custom computation for the 20.11.0 release, so you’ll be able to add the kind of ‘now’ you are talking about and have it auto inserted, server side. Your case here is slightly different to the requests we’ve had already (e.g. auto inserting datestamps into mutations) but it sounds like an interesting case to consider.

Ok sweet. So this does the optimization that I was going to try to do manually. And then running in parallel, :smiley: (I am still switching from synchronous brain logic [php] to asynchronous [javascript])