type Todo {
id: ID!
text: String!
owner: User
}
type User {
status: Int! # allowed to mutate only by the admin (so, not by User)
username: String! @id
todos: [Todo] @hasInverse(field: owner) # allowed to mutate by the owner/user (this is explained by the doc)
}
}
Thanks, but in all those examples the @auth scope is the whole object. How I can scope @auth on the field ‘status’ from my example? In other words, I as admin of my application would like to prevent the user to change his ‘status’ field but allow him to add Todo.
I’m assuming you have more fields for the User type. Because you could lock down this type with @auth rules to only allow mutate by admin which would make the username and status only mutable by an admin and then allow the Todo type to be created/updated/deleted by the direct owner which with the hasInverse directive will keep the edges balanced. So yes, this very simply example is possible you just have to add/update/delete the Todo and not update the User.
What is not possible without some linking-node work arounds:
type User {
username: String! @id
status: Int! # allowed to mutate _only_ by the admin
nickname: String # allowed to mutate by the user _or_ the admin
}
type User {
username: String! @id
nickname: String
adminFields: Administration!
}
type Administration {
status: Int!
}
I do not know if this is a solution since it is not possible to filter the nodes by the edge. So, how the ‘queryUser’ auth query would look like to allow an update for the user with status 0?
So first the inefficiency explained. Auth rules work by checking for the existence of the inner fields which under the hood uses the same process as @cascade so if all fields requested in the rule exist then it equates to true.
But @cascade is inefficient because it is a post query process in the query plan. There is a query plan even though there is no query planner at the moment. This is just understood that the query always follows the same plan, maybe some day we will have a DQL query planner that will make this better. See for reference: Query Planner. Back to the topic at hand. The main problem with cascade is that you could have a rule that queries ALL nodes of a type with deep edges to appease the rule, when more efficiently would be to write the most efficient query to get a truthy value.
Take these triples and imagine we had a few more million users to go along with it, but only these two status nodes. If you wanted to efficiently query the users with the status equal to 0 you would not start at the users, traverse to the filtered status and then post query process to filter those users out. No, instead to be efficient you would start with the smallest universe which would be the filtered status and traverse to the users, and then use those users to appease the rule.
This really gets nitty gritty though because depending on how in GraphQL you filter your query and depending on the data layout, this might actually be less efficient.
So again I reference the link above to the query planner if you want more of my thoughts about the complexity of actually making queries more efficiently which depends on the data, and how do you know the data until you make the query.
hehe, you asked for efficiency and @amaster507 answered…
This is the same thing 1) and 2) here.
This will not work as expected, since you cannot prevent the @hasInverse field from being updated, which keeps the app unsecured.
Your only workaround here is to create a custom mutation that can just update the user information with dql, and lock the whole field otherwise to admins-only. This is why @zmajew is completely right on this thread.