ABAC lambda functions hard to debug when using GraphQL - permissions error

hey, walking through the basic tutorials and gotten stuck on how to debug my Predicate functions for each role.

Essentially, I’m setting the permissions for a for a user to “Create” a recipe.

I’ve “logged in” as a user and am using their auth token, and try to run this query, which works just fine when I blanket allow create access from users to recipes.

`mutation{
  createRecipe(data:{
  title: "Green Curry"
  description: "a normal curry"
  instructions: "dummy instructions"
  user:{
        connect:"<user ID>"
      }
    }){
    _id
  }
}`

However, when I set the following predicate:

Lambda("values", Equals(Identity(), Select(["data", "user"], Var("values"))))

I get the “permission denied” error. I’ve tried every possible recombination, but this is really hard to debug without seeing how my graphQL request is being resolved into a create request in FQL.

even weirder, a similar predicate is being applied to the “READ” permission for the same role for the same document and it works fine

Lambda("ref", Equals(Identity(), Select(["data", "user"], Get(Var("ref")))))

Hi @bengsquared!

From what I can tell, editing relationships is a separate write operation after create. So you need create and write permissions.

Try this:

  privileges: [
    {
      resource: Collection("Recipe"),
      actions: {
        read: Query(
          Lambda(
            "ref",
            Equals(
              Identity(),
              Select(["data", "user"], Get(Var("ref")))
            )
          )
        ),
        write: Query(
          Lambda(
            ["oldData", "newData"],
            And(
              Equals(Identity(), Select(["data", "user"], Var("oldData"))),
              Equals(
                Select(["data", "user"], Var("oldData")),
                Select(["data", "user"], Var("newData"))
              )
            )
          )
        ),
        create: Query(
          Lambda(
            "values",
            Equals(Identity(), Select(["data", "user"], Var("values")))
          )
        )
      }
    }
  ]

You can reference the following topic for some similar discussion.

Thanks @ptpaterson - I have those permissions set as you show above, didn’t work. also read the attached thread.

even with my permissions set so openly like this, I still am unable to perform the operation. It might have to do with how the data is being passed for that given user nested?

ref: Role("User"),
  ts: 1595378772825000,
  name: "User",
  privileges: [
    {
      resource: Collection("recipes"),
      actions: {
        read: true,
        write: true,
        create: Query(
          Lambda(
            "values",
            Equals(Identity(), Select(["data", "user"], Var("values")))
          )
        ),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },

more Schema details, there are no resolver functions for this, it’s just the default resolvers FaunaDB seems to use for mutations when autogenerated from a basic schema

type Mutation{
   createRecipe(data: RecipeInput!): Recipe!
}

input RecipeInput {
  ingredients: RecipeIngredientsRelation
  title: String!
  user: RecipeUserRelation
  description: String
  instructions: String
  time: Int
  image: String
}
  
input RecipeUserRelation {
  create: UserInput
  connect: ID
}

Further updates,

I implemented your rules above, and I am able to UPDATE as long as I don’t include my user: field in my update, and still unable to connect.

Definitely has to do with how the Identity() is being compared to the user ref in the data.

And finally, if there was any doubt, I logged in with that user on fauna shell, ran this command which worked just fine.

Create(Collection("recipes"),{data:{title:"red curry",user:Ref(Collection("users"), "<user_ID>")}})

Using that same secret as a bearer token, running an equivalent graphql query gets me

mutation{
  createRecipe(data:{
  title: "White Curry"
  user:{
        connect:"<user_ID>"
      }

    }){
    _id
  }
}

gets me

{
  "errors": [
    {
      "message": "Insufficient privileges to perform the action.",
      "extensions": {
        "code": "permission denied"
      }
    }
  ]
}

alright here we go,

Still not totally “Solved” but it seems that one of my schema updates must have decoupled one of the default resolver functions, so it was literally pasting the object {connect:"<id>"} in the chef param of the document… eventually after another update it returned the error “expecting set or ref, found Object”

sooo I had to override the schema, rebuild everything from scratch, and now it works like a charm.

the final schema which I uploaded and was heavily filled out by autofill (users changed to “chef” for flair):

  ingredients: [IngredientEntry!] @relation
  title: String!
  chef: Chef! 
  description: String
  instructions: String
  time: Int
  image: String
}

type IngredientEntry @collection(name: "ingrediententries") {
  ingredient: Ingredient!
  recipe: Recipe!
  name: String!
  amount: String
  notes: String
}

type Ingredient @collection(name: "ingredients") {
  name: String! @unique
}

type Chef @collection(name: "chefs") {
  username: String! @unique
  name: String
  recipes: [Recipe!] @relation
  bio: String
  image: String
}

type Query {
  myRecipes(filter: FilterInput): [Recipe!]! @resolver(name: "search_recipes")
  allIngredients: [Ingredient!]! @index(name: "all_ingredients")
  findChefByUsername(username: String!): Chef! @index(name: "find_chef_by_username")
}

input FilterInput {
  searchterm: String
  maxtime: Int
}

type Mutation {
  createChef(input: CreateChefInput): Chef! @resolver(name: "create_chef")
  loginChef(input: LoginChefInput): String! @resolver(name: "login_chef")
}


input LoginChefInput {
  username: String!
  password: String!
}

input CreateChefInput {
  username: String!
  password: String!
}

OK for now, but god forbid I need to update this schema ever in the future…

2 Likes