UDF: Problem with the permissions

I have a problem with a function UpdateProduct.

{
  ref: Role("MyCustomRole"),
  ts: 1622063171900000,
  name: "MyCustomRole",
  privileges: [
    {
      resource: Index("email"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Ref(Ref("functions"), "CreateProduct"),
      actions: {
        call: true
      }
    },
    {
      resource: Collection("users"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Collection("accounts"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Ref(Ref("functions"), "UpdateProduct"),
      actions: {
        call: true
      }
    },
    {
      resource: Collection("orders"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Collection("comments"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Ref(Ref("functions"), "DeleteProduct"),
      actions: {
        call: true
      }
    },
    {
      resource: Collection("products"),
      actions: {
        read: Query(
          Lambda(
            "ref",
            Equals(
              Select(["data", "user"], Get(CurrentIdentity())),
              Select(["data", "user"], Get(Var("ref")))
            )
          )
        ),
        write: Query(
          Lambda(
            ["oldData", "newData"],
            Equals(
              Select(["data", "user"], Get(CurrentIdentity())),
              Select(["data", "user"], Var("oldData"))
            )
          )
        ),
        create: true,
        delete: Query(
          Lambda(
            "ref",
            Equals(
              Select(["data", "user"], Get(CurrentIdentity())),
              Select(["data", "user"], Get(Var("ref")))
            )
          )
        ),
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    }
  ],
  membership: [
    {
      resource: Collection("accounts"),
      predicate: Query(
        Lambda(
          "ref",
          Equals(
            Select(
              ["data", "status"],
              Get(Select(["data", "user"], Get(Var("ref"))))
            ),
            "A"
          )
        )
      )
    }
  ]
}

In such a set up query below just work perfectly

client.query(
	q.Update(q.Ref(q.Collection("products"), "299595354097058305"), {
		 data: {
				......
		  }
	})
)

but I can not call the function UpdateProduct

q.Call('UpdateProduct', { ..... })

getting an error: Insufficient privileges to perform the action.

but if I change the write privileges (collection products) then I can call the function

resource: Collection("products"),
  actions: {
    read: Query(
      Lambda(
        "ref",
        Equals(
          Select(["data", "user"], Get(CurrentIdentity())),
          Select(["data", "user"], Get(Var("ref")))
        )
      )
    ),
    write:true
    )
   .....

 }

I do not understand this.
I do need privileges on write action and I need to call function(UpdateProduct) from client. How can I achieve this?

Could you share the full error and function UpdateProduct
But I believe the issue that your function doesn’t have an assigned role. Basically user able to call the function and all queries inside those functions executes with the same role as the user who calls the function.
You can change the function role that would be used when the function called.
check role param at the doc here CreateFunction | Fauna Documentation

The UpdateProduct function:

{
  name: "UpdateProduct",
  role: Role("MyCustomRole"),
  body: Query(
    Lambda(
      ["data"],
      Let(
        {
          name: Select(["name"], Var("data")),
          desc: Select(["desc"], Var("data")),
          images: Select(["images"], Var("data")),
          category: Select(["category"], Var("data")),
          price: Select(["price"], Var("data")),
          unit: Select(["unit"], Var("data")),
          quantity: Select(["quantity"], Var("data")),
          productId: Select(["productId"], Var("data"))
        },
        If(
          And(
            LT(Length(Var("name")), 30),
            LT(Length(Var("desc")), 500),
            And(
              LT(Count(Var("images")), 5),
              All(Map(Var("images"), Lambda("x", LT(Length(Var("x")), 300))))
            ),
            And(
              LT(Count(Var("category")), 5),
              All(Map(Var("category"), Lambda("x", LT(Length(Var("x")), 20))))
            ),
            And(IsNumber(Var("price")), LT(Var("price"), 1000)),
            LT(Length(Var("unit")), 3),
            And(IsInteger(Var("quantity")), LT(Var("quantity"), 1000))
          ),
          Update(Ref(Collection("products"), Var("productId")), {
            data: {
              user: Select(["data", "user"], Get(CurrentIdentity())),
              name: Var("name"),
              category: Var("category"),
              desc: Var("desc"),
              images: Var("images"),
              unit: Var("unit"),
              quantity: Var("quantity"),
              price: Var("price"),
              currency: "EUR",
              status: "W",
              updated: Now()
            }
          }),
          Abort("invalid data")
        )
      )
    )
  )
}

The error:

{"errors":[{"position":[],
			"code":"call error",
			"description":"Calling the function resulted in an error.",
			"cause":[{"position":["expr","in","then"],
			"code":"permission denied",
			"description":"Insufficient privileges to perform the action."}]}]}

do you need always to update the user property?

Does the product that you are trying to update have a user field the same as user field that you fetched by Get(CurrenyIdentity()) ? because your write predicate compare data.user from oldData and data.user from Get(CurrentIdentity())

Is there any reason to write in this way Select(["data", "user"], Get(CurrentIdentity()))? is it possible to simply keep ref user: CurrentIdentity()

for debugging, run the command below from the same user with the same productId that you used when got the `permission denied error

Let({
 currentUser: Select(["data", "user"], Get(CurrentIdentity())),
 productUser: Select(["data", "user"], Get(Ref(Collection("products"), 'productId')))
}, {
currentUser: Var("currentUser"),
productUser: Var("productUser")
})

The problem is that I do not have an access to

Select(["data", "user"], Get(Ref(Collection("products"), 'productId')))

but I dont know why.

For simplification I have created a simple function:

{
  name: "test",
  role: Role("MyCustomRole"),
  body: Query(
    Lambda(["data"], Get(Ref(Collection("products"), "299595354097058305")))
  )
}

MyCustomRole:

{
      resource: Ref(Ref("functions"), "test"),
      actions: {
        call: true
      }
    }

client side code:

 var client2;
  
  client.query(
    q.Login(
		q.Match(q.Index("email"), "...."),
		{ password: '...',
		  ttl: q.TimeAdd(q.Now(), 3, 'hour')
		}
	)
  )
  .then(function (res) { console.log('Result:', res); 

	  client2 = new faunadb.Client({
		secret: res.secret,
		domain: 'db.fauna.com',
		scheme: 'https',
	  }) 
	  

		//this query give a response without error
		client2.query(	
			  q.Get(
				q.Ref(
				  q.Collection("products"),
				  "299595354097058305")
			  )
		  )
		.then(function (res) { console.log('Result:', res); })
		.catch(function (err) { console.log('Error:', err) })
			

		// this query can not be completed 
           //getting an error: Insufficient privileges to perform the action
		client2.query(
			q.Call('test', {productId:"299595354097058305"}) //we do not need the productId parameter anyway
		)
		.then(function (res) { console.log('Result:', res); })
		.catch(function (err) { console.log('Error:', err) })

	  
	  
	})
  .catch(function (err) { console.log('Error:', err) })

Why the first query working and the second one give an error?
These queries for me seems the same. Probably I am missing something basic.

I also don’t see the problem immediately. I would however advise you to separate roles that you assign to functions and membership roles since it’s a bit strange to assign a role with membership to a function. Nevertheless it should work.

Since I don’t see the problem I’ll ask some more questions/remarks.

  • Does any other call work via a UDF (CreateProduct for example?)
  • In your last simplified example you created a Custom role to provide access to your UDF but it doesn’t have membership so it makes sense that your token (logged in secret) does not have access to call the UDF.
  • How does your account look like?
  • I’m guessing the problem is somewhere in the membership part of your original role but that would also mean that getting products directly shouldn’t work. Is this the only role in your project?
  • Did you delete and add the role again? If that’s the case, your function might be referring to an old role that no longer exists and that might be your problem.

Problem was solved. As you advised I separated roles. So I have changed the function role to None.
Thanks for your help.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.