Permission denied when predicate set for collection, but works when boolean true

I’m getting a permission denied error when the predicate is set, but it’s all ok when I set it to a boolean true. Don’t know what’s wrong. Here’s the details:

I have a post with an author which I create/update in the following way:

Update(Function("NewPost"), {
  body: Query(
    Lambda(
      [],
      Create(Collection("posts"), {
        data: {
          author: Identity(),
          },
        }),
      )
    )
  }
),

But when I try to call this function I get:

Call("NewPost")
Error: call error
{
  errors: [
    {
      position: [],
      code: 'call error',
      description: 'Calling the function resulted in an error.',
      cause: [
        {
          position: [
            'expr',
            'in',
            'create'
          ],
          code: 'permission denied',
          description: 'Insufficient privileges to perform the action.'
        }
      ]
    }
  ]
}

My permissions are as follows:

{
  ref: Role("User"),
  ts: 1594976677881800,
  name: 'User',
  privileges: [
    {
      resource: Function("NewPost"),
      actions: { call: true }
    },
    {
      resource: Collection("posts"),
      actions: {
        read: true,
        write: false,
        create: Query(Lambda("values", Equals(Identity(null), Select(["data", "author"], Var("values"))))),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    }
  ],
  membership: [ { resource: Collection("users") } ]
}

The problem must be the create action predicate, since it works fine when I set it to true.
I check this manually:

Equals(Identity(), Select(["data", "author"], Get(Ref(Collection("posts"), "2179812749812749812794"))))

and it returns true. So I don’t know why the predicate fails.

I tried removing the null parameter from the Identity function, but it didn’t help. I also tried accessing the data by just using ["author"] and not ["data", "author"], also didn’t help.

I don’t know what’s wrong here. I’d appreciate any help.

Hi @jcubed,

would you try this?

 CreateRole({
  name: 'User',
  privileges: [
    {
      resource: Function("NewPost"),
      actions: { call: true }
    },
    {
      resource: Collection("posts"),
      actions: {
        read: true,
        write: false,
        create: Query(Lambda("values", Equals(Identity(), Select(["data", "author"], Var("values"))))),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    }
  ],
  membership: { resource: Collection("users") } 
})

And let me know?

Luigi Servini

1 Like

@Luigi_Servini It said role already exists, so I ran the following instead:

 Update(Role("User"), {
  name: 'User',
  privileges: [
    {
      resource: Function("NewPost"),
      actions: { call: true }
    },
    {
      resource: Collection("posts"),
      actions: {
        read: true,
        write: false,
        create: Query(Lambda("values", Equals(Identity(), Select(["data", "author"], Var("values"))))),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    }
  ],
  membership: { resource: Collection("users") } 
})

I still get the same thing

> Call("NewPost")
Error: call error
{
  errors: [
    {
      position: [],
      code: 'call error',
      description: 'Calling the function resulted in an error.',
      cause: [
        {
          position: [
            'expr',
            'in',
            'create'
          ],
          code: 'permission denied',
          description: 'Insufficient privileges to perform the action.'
        }
      ]
    }
  ]
}

In hopes of creating a good minimal reproducible example (and also because I used the post example, mine is a bit different) I removed some other permissions, so now I can’t even Get(Identity()), so I took the liberty to add this:

    {
        resource: Collection("users"),
        actions: {
            read: Query(Lambda("ref", Equals(Identity(), Var("ref"))))
        }
    },

It didn’t fix it but now I can verify that for a post (that I created when I set the read to true when I made sure the predicate was the problem) the predicate should be working:

Equals(Identity(), Select(["data", "author"], Get(Ref(Collection("posts"), "271285137980785159"))))
true

If I were to bet, since I’m a beginner, I don’t want to jump to conclusions of grandeur bugs, I probably do some basic stuff wrong that’s taken for granted that everyone does that I don’t :crossed_fingers:.

Is there a way to debug the predicate function to maybe return the Var("values")? Maybe it’s something else, or maybe the function throws some error, that can’t be seen?

I also tried regenerating the secret for the user, cause I thought maybe it doesn’t update existing tokens, but it didn’t help.

Hi @jcubed,

bit strange, just tried your code and it works for me:

> Update(Role("User"), {
...   name: 'User',
...   privileges: [
...     {
.....       resource: Function("NewPost"),
.....       actions: { call: true }
.....     },
...     {
.....       resource: Collection("posts"),
.....       actions: {
.......         read: true,
.......         write: false,
.......         create: Query(Lambda("values", Equals(Identity(), Select(["data", "author"], Var("values"))))),
.......         delete: false,
.......         history_read: false,
.......         history_write: false,
.......         unrestricted_read: false
.......       }
.....     }
...   ],
...   membership: { resource: Collection("users") } 
... })
{ ref: Role("User"),
  ts: 1595047258110000,
  name: 'User',
  privileges:
   [ { resource: Function("NewPost"), actions: { call: true } },
     { resource: Collection("posts"),
       actions:
        { read: true,
          write: false,
          create:
           Query(Lambda("values", Equals(Identity(null), Select(["data", "author"], Var("values"))))),
          delete: false,
          history_read: false,
          history_write: false,
          unrestricted_read: false } } ],
  membership: { resource: Collection("users") } }
> 
> 
> 
> [ben@faunadb-qa-5e73f699-0dd1-0ea1-c434-aa366a7a2641 ~]$ fauna shell --secret xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Connected to https://db.fauna.com
Type Ctrl+D or .exit to exit the shell
> Call('NewPost')
{ ref: Ref(Collection("posts"), "271359353582256645"),
  ts: 1595047296040000,
  data: { author: Ref(Collection("users"), "271293683878330887") } }
> 
> Identity()
Ref(Collection("users"), "271293683878330887")
> 

How did you create the user and how did you login into the database?

Thanks,

Luigi

1 Like

I created a super long step-by-step write-up of how I do every little thing, but then I found out I set the User role on the function, changed it to none and thought I solved it and also that the role setting when creating a function is useless, but shouldn’t it be left as User? I don’t get it.

I replaced the long description in the discourse window and maxed out on the undo function, unfortunately. I’ll write something shorter instead:

My workflow looks like this: I create most of the stuff through fauna console and then I update them through the shell on my local windows PC. In this case the user was created as a result of a function called with the server key.

The post collection, the users collection, the NewPost function, the User role were all created in the console. The NewPost function was updated locally with the server key.

I do the login as you’ve shown, i.e. (all on PC shell with server key) I login the user using the server key, I copy the secret, create a new fauna shell with that secret (while I keep the original in the background) and then run all the stuff that keeps failing as we’ve seen.

I agree this is very weird, the thing that boggles my mind the most is that setting the create permission to true works but with the function as the predicate it doesn’t work.

EDIT: In case I wasn’t clear the call to the function works with roles: admin, server, none but fails only on role: User.

EDIT2: So I read the docs for CreateFunction looks like I should use the role setting for privilege escalation only. Makes sense, as I wondered how to achieve this :upside_down_face: :palm_tree: As I predicted, a stupid thing on my part. Thanks @Luigi_Servini for your help.