I'm currently doing some research on how to implement GraphQL in my current project. There's something that concerns me... How do I verify with the database that the information that is being passed it's actually from that user? Both queries and mutations are grabbed from a single endpoint, which then the client is in charge of performing reads and writes to the DB respectively.
While I see how to validate by type (string, id, number, etc...) I don't see a hook or a moment in which I can verify that the possible Ids which are passed are in fact from the current user. E.g:
query {
hero {
name
}
droid(id: "2000") {
name
}
}
How can I check with the db, that the current id, 2000, belongs to the current user?
I'm currently using .NET Core MVC 2 (If that helps).
I would have done something like this using Entity Framework:
_database.Droids.Where(x => x.UserId == userId && x.id == id).FirstOrDefaultAsync();
If that Id doesn't correspond to that user, it would return an empty object. Same goes for an insert operation (Mutation in GraphQL).
Is there a hook in which I can verify the data? How would I do it?
Thanks!
I don't mind if there's an example in NodeJS or C#
Related
I'm learning GraphQL, and in EVERY example that I saw, the signup/login and me calls are in the mutation and query types. Why is that? Example:
type Query {
me: User
}
type Mutation {
login(email: String!, password: String!): String
}
Shouldn't this be in the User type? Since it's a user related thing?
I apologize if this is an opinion based question - and I will close the question if that's the case.
From the spec:
There are three types of operations that GraphQL models:
query – a read‐only fetch.
mutation – a write followed by a fetch.
subscription – a long‐lived request that fetches data in response to source events.
Each operation is associated to a particular type, although both the mutation and subscription types are optional. So a schema might be defined like so:
schema {
query: Query
}
Here, Query refers to a type named Query. By convention, the three types are named Query, Mutation and Subscription -- but they could be named anything else, it doesn't really matter. They do, however, have to be object types and must include at least one field.
These operation types serve as the "root" of a GraphQL operation and are effectively the entry points to your data graph. No other types are exposed at the root level, even if they exist within your schema.
A me or viewer field is often exposed on the Query type in order to allow users to fetched the currently logged in user. Assuming we already have a User type defined, if we add a me field to our Query type, and set the type for that field to User, we enable the consumers of our endpoint to write a query like:
query {
me {
# some set of User fields like id, firstName, etc.
}
}
If we put the me field on the User type instead, a) we would not necessarily have a way to request the field, since it would not be at the root, and b) we would expose a me field everywhere we return a User (for example, a friends field that returns a List of Users) which doesn't make much sense.
The same logic applies for mutations like login -- we want them at the root of our operation, so we place them inside the Mutation type. Whether login should be a mutation or a query, on the other hand, is subjective and depends on your particular use case.
Quick aside around mutations:
If you're used to working with REST, it's common to see operations prefixed with the data sources they are operating on, for example:
POST /articles/{id}/bookmark
And we might be inclined to enforce a similar structure with our mutations, leading to requests like:
mutation {
article {
bookmark
}
}
However, this is totally unnecessary, and in fact breaks convention and makes implementation more complicated. It's sufficient to instead do:
mutation {
bookmarkArticle
}
More on this point can be found here.
This question based on GraphQL schema design. The design is 100% opinion based thing. But let me try answer on your question.
In login mutation answer, client should receives auth token or similar. This token will be send as http header every request for auth. Obviously, this token is not part of User entity. In complicated application you can design mutation answer like that:
{
user: ...
token: ...
}
But for demo application need only token (string) as login mutation answer.
This turns out to be two separate questions.
The first answer here is, if you're making a GraphQL query, you need some way to get objects to start querying. There's not a concept of "static methods" like you might see in Java or other object-oriented languages; these set of initial queries have to be on the root Query type. You could imagine
type Query {
"The currently logged-in user."
me: User!
"Find a user by their email address."
user(email: String!): User
}
You could query other fields on the User object once you have it, but you need some way to get it initially.
query WhatsMyEmail {
me { email }
}
Secondly, all operations that change data are top-level fields on the Mutation type. This is mostly convention – nothing actually stops you from doing whatever you want in your resolver functions – but it has a couple of key impacts. It's easy enough to just query every field on an object and to generate a query like that from the schema:
fragment AllUserFields on User {
id, name, email, delete
}
query WhoAmI {
me { ...AllUserFields } # oops I deleted myself
}
You also have a guarantee that top-level mutations execute in order, but everything else in GraphQL can execute in any order. If your schema allowed this, the email could be either the old or new email address, for example:
query ChangeMyEmail {
me {
changeEmail(email: "new#example.com")
email # is allowed to evaluate first and return old#example.com
}
}
Things like "login" that really are just "actions" that aren't tied to any specific object make more sense to just be attached to Mutation and not any more specific type. (If "login" were a property of User, which one, and how do you find it without already being logged in?)
I am doing a query onto a class where I have a pointer to a User.
I do query.include('byUser') and when I log out the query result it's shown but when I try to get a specific attribute like email. It doesnt exist.
I also first thought it was odd that I have to get the User details by doing:
const userDetails = query.get("byUser").attributes;
Do I have to do .attributes? And also why isn't the email showing up. Everything else seems to show up in the attributes section.
Thanks
Please note that in parse, you can not query the email field of other users. you can see the email field only if you are the signed in user. This is a security mechanism.
A. If you want to get the email field for a user object, you can do two things:
Pass the user session token to the query.
new Parse.Query(Parse.User).get(<userId>,{sessionToken:<userSessionToken>});
Use master key. (Note: You have to set the master key before.)
new Parse.Query(Parse.User).find({useMasterKey:true});
B. Your include method is correct and it it will fetch the byUser object. You do not need to use .attributes.
UPDATE:
You can also set a publicEmail field in your User class which will not be filtered out by parse-server. to automate this, you can write a cloud code.
Cloud Code Example (on parse-server V 3.0.0 and above):
PLEASE NOTE THAT YOU CAN NOT USE ASYNC FUNCTIONS FOR PARSE-SERVER < V 3.0.0
Parse.Cloud.beforeSave(Parse.User, async req=>{
if (!req.original && !req.master && req.object.get('email')){
req.object.set('publicEmail',req.object.get('email'));
}
})
Now, if new user sign up, this cloud code will automatically adds a new field to the user object publicEmail which is not filtered by parse-server.
I implemented on a website a form to create firebase users and add a node in the firebase database for the user based on a selected State in the form.
So for example, if the user chooses 'Hawaii' in the form and then create the account, the account information will be stored in "Hawaii/id" in the firebase db.
// JSON structure
{
"Hawaii": {
"place1Id": {
//infos
},
"place2Id": {
//infos
}
},
"New York": {
"place1Id": {
//infos
},
"place2Id": {
//infos
}
}
}
My problem is how to make sure that later on when the user will add information to his account, with provided credentials from the previous account creation, this information will be stored in the correct node (Hawaii for example)
I have tried to make a comparison between current user id and keys from States nodes but my database is quite large (and will become larger) so it is taking up to 10 seconds for the code to determine in which node of the database the user is.
And the same process has to occur on each page so it is not the good solution.
var placesRef = firebase.database().ref();
placesRef.once("value", function(snpashot) {
if (snpashot.child("Hawaii").hasChild(user.uid)) {
console.log("Place is in Hawaii");
activiteRef = firebase.database().ref().child("Hawaii").child(user.uid);
}});
Can you please help me figure this out?
Thanks!
If you're keying on uid, you don't need to do anything more than the line you already have:
activityRef = firebase.database().ref().child("Hawaii").child(user.uid);
This is the direct reference to the node you want (if I'm understanding you correctly). You can read the data:
activityRef.once('value').then(snap => console.log(snap.exists, snap.val());
Which will be null if the user has never written there, but will contain data otherwise. You can also perform other operations like update() to change the data at this location.
There's no need to perform the top level query to check if the node already exists -- you can just read it directly.
I'm having a big deal - the meteor app I've been developing the last weeks is finally online. But, for an update, I need to add a field to my users profile.
I thought that walling a methods with the following code would work :
updateUsrs_ResetHelps: function(){
if(Meteor.users.update({}, {
$set: {
'profile.helps': []
}
}))
console.log("All users profile updated : helps reset");
else
throw new Meteor.Error(500, 'Error 500: updateUsrs_ResetHelps',
'the update couldn\'t be performed');
}
The problem is that my users have the classic Meteor.accounts document, whith emails, _id, services, profile, etc... but, in the profile, they don't have a .helps fields. I need to create it.
For the future users, I've modified the accounts creation function to add this fields when they sign up, but for the 200 users I already got signed up, I do really need a solution.
EDIT : Might it be because of the selector in the update ? Is a simple {} selector valid to update all the users / documents of the collection at once ?
From the Mongo documentation (http://docs.mongodb.org/manual/reference/method/db.collection.update/):
By default, the update() method updates a single document. Set the
Multi Parameter to update all documents that match the query criteria.
If you've already taken care of adding the field for new users and you just need to fix the old ones, why not just do it one time directly in the database?
Run meteor to start your application, then meteor mongo to connect to the database. Then run an update on records where the field doesn't already exist. Something like:
db.users.update({"profile.helps": {"$exists": false}}, {"$set": {"profile.helps": []}}, {multi:true})
The Mongo documentation specifies the multi parameter as:
Optional. If set to true, updates multiple documents that meet the
query criteria. If set to false, updates one document. The default
value is false.
I'm not able to use the node server debugger so I'm posting here to see if I can get a nudge in the right direction.
I am trying to allow multiple users to edit documents created by any of the users within their specific company. My code is below. Any help would be appreciated.
(Server)
ComponentsCollection.allow({
// Passing in the user object (has profile object {company: "1234"}
// Passing in document (has companyId field that is equal to "1234"
update: function(userObject, components) {
return ownsDocument(userObject, components);
}
});
(Server)
// check to ensure user editing document created/owned by the company
ownsDocument = function(userObject, doc) {
return userObject.profile.company === doc.companyId;
}
The error I'm getting is: Exception while invoking method '/components/update' TypeError: Cannot read property 'company' of undefined
I'm trying to be as secure as possible, though am doing some checks before presenting any data to the user, so I'm not sure if this additional check is necessary. Any advice on security for allowing multiple users to edit documents created by the company would be awesome. Thanks in advance. -Chris
Update (solution):
// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
// Gets the user form the userId being passed in
var userObject = Meteor.users.findOne(userId);
// Checking if the user is associated with the company that created the document being modified
// Returns true/false respectively
return doc.companyId === userObject.profile.companyId;
}
Looking at the docs, it looks like the first argument to the allow/deny functions is a user ID, not a user document. So you'll have to do Meteor.users.findOne(userId) to get to the document first.
Do keep in mind that users can write to their own profile subdocument, so if you don't disable that, users will be able to change their own company, allowing them to edit any post. You should move company outside of profile.
(If you can't use a proper debugger, old-fashioned console.log still works. Adding console.log(userObject) to ownsDocument probably would have revealed the solution.)