In DQL, is there a canonical way to reference an untrusted object id where the transaction fails if the uid doesn’t exist or a query for the uid is empty?
I have dgraph running behind an authenticated API. As part of the verification process, I am able to query for the user and get the dgraph uid for that user. I trust that uid. Some of my endpoints also take unvalidated uids. How can I make sure those uids are safe to use as the object of a triple?
As an example, I have a ‘favorite’ endpoint where I would like to accept the uid of a specific kind of type (“Book” for example), and add it to the user’s favorites list. The book’s uid is not trusted. So for example (assume user is 0xaa, and 0x578 is untrusted):
favorites: [uid] @reverse .
upsert {
query{
v as var(func: uid(0x578)) @filter(eq(dgraph.type, "Book")) {} # 0x578 unvalidated from the API and is not an existing book
}
mutation {
set {
<0xaa> <favorites> uid(v) # 0xaa was validated by a query that took a session id and token
}
}
}
When run, a new node will be created that will have a new uid and no other type info. Ideally, I could run the mutation only when 0x578 is an already existing book. Originally I tried the set without the conditional upsert, but saw similar behavior where the invalid uid caused a new node to be created.
I have considered using UUIDs but that wouldn’t fix the fact that a wrong value would create an edge to a new node.
It looks like I’ll need to run two transactions, one query to validate the uid and then another to set the edge. Is there a better way?
I’m not sure if I get it. What means “untrusted object id” besides the obvious meaning, in Dgraph context?
unvalidated in what sense? there’s no validation for UIDs. UIDs are uint64. Which are fixed values but assigned on “demand”. There’s no validation process. That’s why it sounds confusing to me. Looks like you are talking about your architecture instead of Dgraph’s.
So, I’ll assume there is some complex process abstractly speaking, on your end, that I don’t know how it works. But it uses Dgraph’s UID and probably has a list of untrusted and trustful known UIDs.
So, looks like the process is tied to the same cluster. Right? not a remote service or something.
Well, It depends. An Upsert block can have as many query blocks as you need. And as many mutations and conditional mutations as you need. It really depends on your architecture.
Empty/dangling nodes are created when you don’t get how the upsert block works in relation to your architecture. If it depends on Dgraph, the solution is possible but some more context is necessary. If it is something that you created or others. That’s hard to tell.
If I get it. It would be something like
upsert {
query{
v1 as var(func: uid(0xaa)) #apply some validation here if this can be possible in DQL
v2 as var(func: uid(0x578)) @filter(eq(dgraph.type, "Book"))
# 0x578 unvalidated from the API and is not an existing book
}
mutation @if(eq(len(v1), 1) AND eq(len(v2), 1)) {
set {
<0xaa> <favorites> uid(v2) # 0xaa was validated by a query that took a session id and token
}
}
}