I'm building a sample app to evaluate FaunaDB and Nextjs
My plan is to have the web app authenticate separately, then create the user on FaunaDB Afterwards create a token on FaunaDB, and allow the user to connect through his own secret token
I believe I'm on the right track to get this model to work, but I'm facing an issue with the Custom-role in FaunaDB
The data model is User -> Board -> Tasks, and I will use the access to boards in this question
Here is the code for the custom role
{
ref: Role("Free_Tier_Role"),
ts: 1601934616790000,
name: "Free_Tier_Role",
membership: [
{
resource: Collection("user"),
predicate: Query(
Lambda("ref", Select(["data", "isEnabled"], Get(Var("ref"))))
)
}
],
privileges: [
{
resource: Collection("user"),
actions: {
read: true,
write: false,
create: false,
delete: false,
history_read: false,
history_write: false,
unrestricted_read: false
}
},
{
resource: Collection("board"),
actions: {
read: Query(
Lambda(
"ref",
Equals(Identity(), Select(["data", "owner"], Get(Var("ref"))))
)
),
write: Query(
Lambda(
["oldData", "newData"],
And(
Equals(
Select("id", Identity()),
Select(["data", "owner"], Var("oldData"))
),
Equals(
Select(["data", "owner"], Var("oldData")),
Select(["data", "owner"], Var("newData"))
)
)
)
),
create: Query(
Lambda(
"newData",
And(
Equals(Identity(), Select(["data", "owner"], Var("newData"))),
LT(Count(Match(Index("board_by_owner"), Identity())), 3)
)
)
),
delete: Query(
Lambda(
"ref",
Equals(Identity(), Select(["data", "owner"], Get(Var("ref"))))
)
),
history_read: false,
history_write: false,
unrestricted_read: false
}
},
{
resource: Collection("task"),
actions: {
read: Query(
Lambda(
"ref",
Equals(Identity(), Select(["data", "owner"], Get(Var("ref"))))
)
),
write: Query(
Lambda(
["oldData", "newData"],
And(
Equals(
Select("id", Identity()),
Select(["data", "owner"], Var("oldData"))
),
Equals(
Select(["data", "owner"], Var("oldData")),
Select(["data", "owner"], Var("newData"))
)
)
)
),
create: Query(
Lambda(
"newData",
And(
Equals(Identity(), Select(["data", "owner"], Var("newData"))),
LT(Count(Match(Index("task_by_owner"), Identity())), 10)
)
)
),
delete: Query(
Lambda(
"ref",
Equals(Identity(), Select(["data", "owner"], Get(Var("ref"))))
)
),
history_read: false,
history_write: false,
unrestricted_read: false
}
},
{
resource: Index("task_by_owner"),
actions: {
unrestricted_read: false,
read: false
}
},
{
resource: Index("board_by_owner"),
actions: {
unrestricted_read: false,
read: false
}
}
]
}
The problem I'm facing is When I login through a user token, and that user is the owner for a board, I get an empty list
> Map(Paginate(Documents(Collection('board'))),Lambda('x', Get(Var('x'))))
{ data: [] }
To test they have the same value, I'm running this command on the shell on the dashboard
Select(["data", "owner"], Get(Ref(Collection("board"), "278575744915866117")))
Ref(Collection("user"), "278571699875611143")
>> Time elapsed: 28ms
And run the Identity() on my token-authenticated instance
> Identity()
Ref(Collection("user"), "278571699875611143")
>
P.S. before this approach, I was matching the id number only using Select(['data', 'ownerId'], Ref) but it didn't work, even when I tried converting both ToString or ToNumber
Wow, took me about 2 days to diagnose what was going on
Basically, the code I've written above is working to only allow an owner of a board or task to see his/her own items, and update them
But, the problem for me was with membership, it was not working as expected
Here's the solution steps you need to follow if you find yourself with non-working permissions
Make sure the role applies to your user Here's how I did it
Query(Lambda("x", Add(Var("x"), Var("x"))))
Call("test_newFunc", 2)
Lambda("docRef", Equals(true, Select(["data", "isEnabled"], Get(Var("docRef")))))
Which means, the user must have a data field "isEnabled", and its value must be true
Then test by switching the value of this field for the impersonated user until you confirm that this role is being applied
Once this step is clear, then you can test the predicates for each permission
Hope this is helpful for future developers who run into this issue