How to enforce row-level insert and update permissions in Hasura based on an arbitrary user property - hasura

Here is a description of my tables
event {
id
organization__id
}
user {
id
}
organization {
id
}
organization_user {
user__id
organization__id
}
How can I restrict a user in order to allow him to insert/update event with only organization__id he belongs (through table organization_user)?

You definitely do not need to add any token session variable values other than an X-Hasura-User-Id which I assume you have already implemented.
Using Hasura correctly is all about making the proper relationships between your different tables.
It is important to remember, Hasura can use both object and array relationships for row level permissions lookups.
I am going to assume that the following relationships exist (if they do not, you can create them easily).
event => organization
organization => orgUsers (organization_user array relation)
organization_user => user
Event then has a direct lookup path to user.id and you can check that it equals the X-Hasura-User-Id. Although this is an _eq check, remember: the orgUsers array lookup in that path makes sure that if any orgUser.user.id matches, they will have row level access.
Comment if you need clarification of any of these points.

It's hard to say without knowing you permissions and authentication scheme, but you can accomplish this by using session variables in your row-permissions settings (docs).
First, you'll need your users to have a value in their session token that maps to the organization__id column in your database. For example, suppose your session tokens have a property called X-Hasura-Organization-Id.
Next, disable insert and update permissions on the organization__id column in the event table.
For insert permissions, configure a column preset which sets the organization__id value based on the user's session token value for X-Hasura-Organization-Id.
And, finally, to prevent users from updating records outside of their organization, you can configure a row-level permission rule which checks that the X-Hasura-Organization-Id in the user's session token matches the organization__id of the row they wish to update.

Related

How to write to a document and read the id of it within a single transaction in Firestore?

I am doing the user authentication where I have this case:
Read from vendor_type document and if it returns null(doesn't exist) then continue the transaction,
Create new user using .auth().createUserWithEmailAndPassword(email,password),
Read the new users ID,
Write to vendor_type document some of the new user's detail such as name, surname, userId -->> userId is the problem, how can I create a user and get the ID within a single transaction, can I even do that? ,
Take the newly created ID of the user, and create a new vendor document with that ID.
So far I don't have any code to post because I don't know if this is even gonna work so I didn't start. If you have any idea how to implement this, please let me know. The main issue is getting the user ID while still in the transaction.
At the time of writing, it is not possible to combine in one transaction the creation of a user through the createUserWithEmailAndPassword() method from the Auth service AND a write to the Firestore service.
They are two different services offered by Firestore and therefore you cannot combined calls to these two different services in one transaction.

NoSQL query of items,lists, Groups and Users using Firebase

Am looking at the data structure in this post and want to know how you would go about getting the emails of users who belong to a certain group when they could belong to several groups and the GroupID stored against that user is the current group they are participating in?
Do you store the email addresses with the userid under the "members" or, instead, for each member of the group, get that user's email address from the "users" document userid (this would mean iterating through the group/members collection and doing a query for each user. Not very efficient).
Am used to SQL so this is all new to me.
You should have a single node for each user
/users/UID/emails/
/users/UID/emailunread/
/users/UID/settings/
/users/UID/details/
/users/UID/payments/
So you can simply do a subscription for a singular node path this.myDatasubscription = this.DB.list('users/' + this.uid).snapshotChanges() ensuring changes like new emails or account settings will detected and rolled out in real time back to the app, so your are using angular/ng or something similar client side then your variables {{this.email_list}} should update real time with no page changes.
Take a look at this one.
error: Property 'getChildren' does not exist on type 'DataSnapshot'

Firebase database manage unregistered users

I'm working on a project where I have to handle unregistered user - users that have been added to the group but still they do not have registered in the app.
What I'm doing now is to create a new child in my 'user' db, putting all the info that i know about this unregistered user.
Of course, it also has an id.
This id will be used to represent that user and so it will be used in a lot of places of the db.
The problem comes when this user tries to register itself. Since when creating a new user it's not possible to force the 'id' that he already had, Firebase will create a new id for him.
Then, in the db I need to change all the references of the 'old id' with the new one.
Is there any better way to do it ?
1) You can use another "fake" table to remap the IDs, that is, instead of changing the old id and its references you can add new instance to your "fake" table when user registered. And when needed using simple service you can find the corresponding id.
2) Secondly, you can do authentication yourself, what I mean is that, you can develop your own registration service and define the id yourself in registration. If system is already big and hard to change. First option would be suitable but will have some cost in terms of time.

How to entirely skip validation in simple schema and allow incomplete documents to be stored?

I'm creating an order form and a schema defined for an Order (certain required fields such as address, customer info, items selected and their quantities, etc).
a. User visits site.
b. A unique ID is generated for their session as well as a timestamp.
var userSession = {
_id: createId(),
timestamp: new Date(),
};
var sessionId = userSession._id;
c. The userSession is placed in local storage.
storeInLocalStorage('blahblah', sessionObject);
d. An Order object is created with the sessionId as the only field so far.
var newOrder = {
sessionId: sessionId;
};
e. Obviously at this point the Order object won't validate according to the schema so I can't store it in Mongo. BUT I still want to store it in Mongo so I can later retrieve incomplete orders, or orders in progress, using the sessionID generated on the user's initial visit.
This won't work because it fails validation:
Orders.insert(newOrder);
f. When a user revisits the site I want to be able to get the incomplete order from Mongo and resume:
var sessionId = getLocalStorage('blahblah')._id;
var incompleteOrder = Orders.findOne({'sessionId', sessionId});
So I'm not sure how to go about doing this while accomplishing these points.
I want full simpleschema validation on the Orders collection when the user is entering in items on the forms and when the user is intending to submit a full, complete order.
I want to disable simpleschema validation on the Orders collection and still allow storing into the DB so that partial orders can be stored for resumption at a later time.
I can make a field conditionally required using this here but that would mean 50+ fields would be conditionally required just for this scenario and that seems super cumbersome.
It sounds like you want to have your cake, and eat it too!
I think the best approach here would be keep your schema and validation on the Orders collection, but store incomplete orders elsewhere.
You could store them in another collection (with a more relaxed schema) if you want them on the server (possibly for enabling resume on another device for the logged in user) , or more simply in Local Storage, and still enable the resume previous order behaviour you are wanting.
Only write to the Orders collection when the order is complete (and passes validation).
Here's a variation on #JeremyK's answer: add an inProgress key to your order of type [Object]. This object would have no deeper validation. Keep your in progress order data in there until the order is final then copy/move all the relevant data into the permanent keys and remove the inProgress key. This would require that you make all the real keys optional of course. The advantage is that the object would maintain its primary key throughout the life cycle.
I think this particular case has been solved; but just in case, you can skip Simple Schemma validations by accessing MongoDB native API via Collection#rawCollection():
Orders.rawCollection().insert(newOrder);
While this question is very old in the meantime there is a better solution. You probably use simple schema together with collection2. Collection2 has the ability to set multiple schemas based on a selector and then validate against the correct schema based on it.
https://github.com/Meteor-Community-Packages/meteor-collection2#attaching-multiple-schemas-to-the-same-collection
e.g. you could have a selector {state: 'finished'} and only apply the full schema to these documents while having another selctor, e.g. {state: 'in-progress'} for unfinished orders with a schema with optional fields.

Meteor: Disallow user from selecting id's when inserting documents

When using mini-mongo to insert documents into a collection a user could pass the _id field and that _id will be set on the document as long as it is a string or an ObjectID (and doesnt collide with an existing id). That doesnt seem sensible to me. I want the _id of the documents to be generated by the server, always, so I dont end up with a db where both strings (potentially of different length) and ObjectIDs exist as _ids.
Is there a simple way to achieve this? Right now the best I can think of is checking the id in a deny rule (where the _id will either already be set by Meteor or be the value the user provided) and if it's not the type/length I want it to be I change it, but that also requires checks to avoid duplicate ids. Not too difficult but seems overly complex for something as basic as this.
Reviewing my response, here's your answer:
Deny all inserts. Create a method, collectionInsert(document), that checks if an _id field is specified.
Meteor.methods({
collectionInsert: function(document) {
if (document && document._id) {
delete document._id;
}
// Returns the _id generated
return collection.insert(document);
}
});
In today's versions of meteor (0.8 and higher) the server and client generate the same _id in this case anyway. But using this method should ensure that the client simulates the field insert and that the server's version of the generated _id is authoritative.
The correct way to do this is probably to do the insert in a Meteor.method. You can't trust client-generated IDs in high-risk applications, so using collection#insert with allow/deny hooks isn't sufficient. Instead, call a method to do the insert, which will always create a server-generated ID.
You can still stub this method on the client, for latency compensation, but the inserted ID will be overwritten when the server call returns.

Resources