Search code examples
faunadb

ABAC Permissions don't work the same for read and write


I'm currently checking out FaunaDB creating a sample Goal tracker (todo!) application. Using graphQL, I have created the schema as:

type Goal {
  title: String!
  completed: Boolean!
  owner: User!
}

type User {
  name: String!
  username: String! @unique
}

input GoalInput {
  title: String!
  completed: Boolean!
}

I would like to set the ABAC permissions as:

  1. Logged in user can view Goals created by themselves.
  2. Logged user can edit only Goals created by themselves.

The permissions for role has been set as:

{
   resource: Collection("Goal"),
   actions: {
     read: Query(
       Lambda("goalRef", Equals(Select(["data", "owner"], Get(Var("goalRef"))), Identity()))
     ),
     write: Query(
       Lambda("goalRef", Equals(Select(["data", "owner"], Get(Var("goalRef"))), Identity()))
     )
   }
}

While the read part works well, the write part, not so much. updateGoal (mutation which is automatically created by FaunaDB) keeps throwing the insufficient permissions error.


Solution

  • Just uploading your provided schema, here is the GoalInput generated:

    input GoalInput {
      title: String!
      completed: Boolean!
    }
    

    If you cannot create new goals with the owner field, then you cannot query them -- owner will be null

    Try to update your schema to include goals field for the user, in order to get the one-to-many relation you are looking for. (fauna docs)

    type User {
      name: String!
      username: String! @unique
      goals: [Goal] @relation
    }
    

    Then make sure every time you create a new Goal, you set the owner to yourself.

    Alternate using functions

    You could create a custom mutation to createGoalForMe which would automatically set the owner field as you.

    Make sure that you give call privileges to the new function.

    Check Permissions

    The write permission lambda receives both a newData and oldData as arguments.

    /*...*/
    {
      resource: Collection("Goal"),
      actions: {
        /*...*/
        write: Query(
          Lambda(
            ["oldData", "newData"],
            And(
              Equals(Identity(), Select(["data", "owner"], Var("oldData"))),
              Equals(
                Select(["data", "owner"], Var("oldData")),
                Select(["data", "owner"], Var("newData"))
              )
            )
          )
        )
      },
      /*...*/
    }
    /*...*/
    

    So change "goalRef" to ["oldData", "newData"] and I think you should have it.