Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing down a context from a node to its descendents for checking permissions. #375

Closed
joonhocho opened this issue May 3, 2016 · 6 comments

Comments

@joonhocho
Copy link

joonhocho commented May 3, 2016

Say that I have a user object.

Here's how it looks.

user: {
  info: {
    email: {
      address: String!
    }
  }
  friends: [ID]!
}

When User A tries to access User B's info.email.address, A's ID must be present in the B's friends field.
When resolving address field of email object, it currently has no way to access the friends field of its owner user B (unless I am missing something).

I wish there is a way to add some context value to pass down to its subtree nodes and their fields so they can use it when resolving.
For example, user object type wants to add itself as a context value for its descendent node fields, and the fields resolvers can access it as context.user.
This is very similar to how context works in React via childContextTypes and getChildContext.

@joonhocho joonhocho changed the title Passing down a context from tree parent to its descendents for Permission. Passing down a context from a node to its descendents for checking permissions. May 3, 2016
@joonhocho
Copy link
Author

For referece of how React's context works. https://facebook.github.io/react/docs/context.html

@dschafer
Copy link
Contributor

dschafer commented May 3, 2016

#301 has a discussion of this and why we didn't add the ability. In particular, having different contexts in the same query causes major issues for client caches, which can now see conflicting values for the same object and field.

However, https://github.com/graphql/graphql-js/blob/master/src/graphql.js#L43 allows the caller to pass down a context for the entire query. This context is most commonly used to contain authentication information.

So the resolver for address could know that the email is for user B, and get authentication info for user A, and use that to determine whether it is visible.

Hope this helps!

@dschafer dschafer closed this as completed May 3, 2016
@joonhocho
Copy link
Author

What if the query looks like this?

users {
  info {
    email {
       address
    }
  }
}

In this case address resolver does not know which user it belongs to, because the query is querying for multiple users.

@joonhocho
Copy link
Author

@dschafer, in case you don't get notified.

@dschafer
Copy link
Contributor

dschafer commented May 3, 2016

If the ability to see the address depends on whose address it is, I would expect the object returned by email to have a reference to the User; at that point, you would do:

address: {
resolve: (email, _, context) => context.isFriendsWith(email.getUser()) ? email.getAddress() : null
}

Though better than that would be to have that logic live inside the email business object:

class Email {
getAddress(context) {
return context.isFriendsWith(getUser()) ? this.address : null;
}
getUser() {
return this.user; // The email knows about the user because the address visibility depends on it
}
}

address: {
resolve: (email, _, context) => email.getAddress(context)
}

This way, every caller of Email.getAddress() has the desired visibility rules applied.

@joonhocho
Copy link
Author

Thank you for your answer!
So far, I've been using plain javascript objects for resolving data trying to go as vanilla as possible.
Now I see the need for wrapping data objects with model classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants