This, to me, is the most basic authentication scheme for user-generated content, given a collection called "posts":
Allow any authenticated user to insert into "posts" collection
Allow the user who inserted the document into collection "posts", to read, update, and destroy the document, and deny all others
Allow the user to list all documents in collection "posts" if they are the one who created the documents originally
All examples I've found so far seem to rely on the document ID being the same as the user's id, which would only work for user's "profile" data (again, all the examples seem to be for this single limited scenario).
It doesn't seem that there is any sort of metadata for who the authenticated user was when a document was created, so it seems i must store the ID on the doc myself, but I haven't been able to get past this point and create a working example. Also, this opens up the opportunity for user's to create documents as other users, since the user ID is set by the client.
I feel like I am missing something fundamental here since this has to be the most basic scenario but have not yet found any concise examples for doing this.
This answer is from this github gist. Basically, in the document collection posts there is a field called uid and it checks if it matches the users uid.
// Checks auth uid equals database node uid
// In other words, the User can only access their own data
{
"rules": {
"posts": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
-- Edit --
DSL rules
match /Posts/{document=**}{
allow read : if uid == request.auth.uid;
allow write: if uid == request.auth.uid;
}
Related
I am playing around with CouchDB and PouchDB for a project where users have their own database and can add each other to view, or edit their docs.
Goal is to have different levels of accessibility: Depending on the docs themselves other users, who are not the docs.owner will have limited writing/updating permissions. The databases owner/admin grants those privileges.
I am not sure how to properly implement that.
At the moment my solution for this is to let the DBs owner "befriend" other users and add them as members to db/_security, while limiting the writing rights with a _design document like described in: https://github.com/pouchdb-community/pouchdb-authentication/blob/master/docs/recipes.md
But I need a mixture of user specific and DBs specific permissions. So my strategy is to also let the user/owner add special roles besides the default "members" and "admins" to db/_security.
Example:
A user paula owns the DB paulas_DB and wants to grant user jan the right to change the property "location" of every document.
So Paula adds jan to members.names in _security and adds a new list to _security called "movers":
curl -X PUT $HOST/paulas_DB/_security -d '{"members":{"names":["admin","paula","jan"],"roles":[]},"admins":{"names":["admin","paula"]},"movers":["jan"]}'
the docs in paulas_DB are structured like this:
{
"_id": "y",
"_rev": "7-x",
"owner": "paula",
"location": "somewhere",
"name":"thing"
}
now there there is a design document in place in her database, checking that anyone who wants to change the document in general is at least a member AND then checking if they want to change location like this:
function (newDoc, oldDoc, userCtx, secObj) {
// only admins owners or friends of the owner (aka users in the _security members.names list) can edit
if (userCtx.roles.indexOf('_admin') === -1 && oldDoc.owner !== userCtx.name && secObj.members.names.indexOf(userCtx.name) === -1)
{
// next step: add special fields to be either editable or not
throw({forbidden : "sorry. you are not the owner of this document"});
}
// only owners users who are also listed within _security.movers can change the location
if (oldDoc.location !== newDoc.location && oldDoc.owner !== userCtx.name && secObj.movers.indexOf(userCtx.name) === -1)
{
throw({forbidden : "you are not allowed to change the location of an item, dummie!"})
}
}
This method seems to work and was fairly straight forward, but something feels off to add non-standard properties into _security.
Is there another, propper way to do the same thing? Or is that an acceptable design for a document/user specific permission system?
I'm following a tutorial about firestore but I don't understand firestore rules very well. I'm trying to allow anyone to be able to create in the standard
users/uid/
path but only allow updates if the requester is trying to update
users/theirUserId/
I saw this in the documentation, but it didn't seem to work for me:
allow write: if request.auth.uid == resource.data.author_id;
Can anyone explain the functionality of the above line and/or offer suggestions as to how I can achieve this?
Additionally, is there any way to specify rules for a specific piece of data within a document?
It looks like that your document doesn't contain a author_id field.
The Firebase documentation Writing Conditions for Security Rules use this example:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches the 'author_id' field
// of the document
match /users/{user} {
allow read, write: if request.auth.uid == resource.data.author_id;
}
}
}
It means that a random user will be able to read and write in the users collections only if their authentication ID equals the author_id field of a specific document.
The resource variable refers to the requested document, and resource.data is a map of all of the fields and values stored in the document. For more information on the resource variable, see the reference documentation.
For your second question, I recommend you to have a look on the documentation about resource variable (link in the quote above). It is the same logic as your author_id question.
You can split allow write in to three create, update, delete for specific cases.
In your case
allow create: if request.auth.uid != null;
allow update: if request.auth.uid == resource.data.author_id;
which says any authenticated users can create and only update their on document. and created user must have a field author_id which is their user id.
Im trying to set up rules for a Firestore Database.
Im having some trouble setting up rules for nested objects.
The database structure looks like this:
Users ( collection of User objects )
-----userDocument ( the name of the document matches the auth-users uid )
-----------------users ( subcollection of users )
-------------------------userdocument ( the name of the document matches the auth-users uid )
note: the last userDocument does not contain any references.
He has his own document in the main Users collection.
I want every user to have read/write access to every user in the Users collection, who has a matching ID of the users in his subcollection of users.
Furthermore, any user should be able to create a new user on the database, as long as they are authenticated with firebase Auth.
I have tried following solutions, it doesnt work:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}{
allow read, write: if exists(/databases/$(database)/documents/users/userId/users/$(request.auth.uid)) || userId == request.auth.uid;
}
}
}
What i need is:
A way to get all document names from the logged in userĀ“s subcollection of users
A way to grant access to ONLY these users
The user has 1 user in his subcollection, so the user should have access to read/write his own user, and qb2pa1TWXHZr0NZUREealgWrOYb2.
I found a solution that works, i hope this helps someone in the future.
Everything is fully tested, commented and working.
service cloud.firestore {
//This is the "root" of the database. From here we can match into our collections.
match /databases/{database}/documents {
//matching the collection "users", the wildcard "userId" is used for the user we will be working with.
match /users/{userId}
{
//Everyone is allowed to write, if they are logged in.
allow write: if request.auth.uid != null;
//A user is allowed to read, update and delete his own account.
allow read, update, delete: if request.auth.uid == userId;
//A user is allowed to read a user, if the user matching "userId" exists in the logged in users own subcollection of users.
allow read: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/users/$(userId));
//Matching the subcollection "users", still in the user matching userId.
match /{users=**}{
//A user is allowed to read, write, update, delete in the subcollection on his own account.
allow read, write, update, delete: if request.auth.uid == userId;
//A user is allowed to read, write, update, delete in the subcollection,
//if the user matching "userId" exists in the logged in users own subcollection of users.
allow read, write, update, delete: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/users/$(userId));
}
}
//matching the collection "duties", the wildcard "dutyId" is used for the duty we will be working with.
match /duties/{dutyId}{
//Everyone is allowed to write, if they are logged in.
allow read, write: if request.auth.uid != null;
// A user is allowed to read, write and update if the string in the field "ownerId" in the duty matching "dutyId" == the users Uid.
allow read, update: if resource.data.ownerId == request.auth.uid;
//A user is allowed, if the user matching "ownerId" exists in the logged in users subcollection of users.
allow read, update, delete: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/users/$(resource.data.ownerId));
}
}
}
I'm trying to model a profiles database, where every profile is public for authenticated users, and where each user can only update his/her own profile.
Each profile document _id will be the email of the registered user, and I modeled the following validate_doc_update function:
function(newDoc, oldDoc, userCtx, secObj) {
var id = userCtx.roles[0].substring(5);
if (newDoc._id !== id) {
throw({forbidden: "One can only update one's self document."});
}
}
I tested the database and it worked perfectly as I expected. Am I getting this right? Is there any flaw or breach?
(I'm using SuperLogin for creating and login-in the users)
Yes, this is a viable solution. I'd check for the roles prefix to be "user:" just to be safe, and maybe you can also allow users with the role "_admin" to edit any document.
One possible problem with your solution might be that you are probably exposing the email addresses of all your users if you use the email as the id. But if you're ok with that in your particular use case, your solution is fine.
The key here is SuperLogin. It creates a document in the _users database to represent a session of a given user.
When the user logs in, SuperLogin creates a new session that inserts a document that looks like this:
{
"_id": "org.couchdb.user:iwn9IpwNR4i0wrxmYcGarg",
"_rev": "1-0f36c9e220c41fe54726cdd01adcdcf2",
"password_scheme": "pbkdf2",
"iterations": 10,
"type": "user",
"name": "iwn9IpwNR4i0wrxmYcGarg",
"user_id": "aasaaaaaaaaaaaaaaaaaaaandremiramor#gmail.com",
"expires": 1458027109739,
"roles": [
"user:aasaaaaaaaaaaaaaaaaaaaandremiramor#gmail.com",
"user"
],
"derived_key": "9a6cfaaac2249ef74fba599c3fbede65a48dcd32",
"salt": "1ef2689337699061b9460f6f68f63f28"
}
The roles array is also created by SuperLogin using the username. Since I had it configured to use the email as the username ({emailUsername: true}), the e-mail was could be matched as the corresponding document _id.
I'm trying to create a simple todo or blog system based on React + ReactFire.
And after a hour of reading firebase tutorial confused about configuring firebase security rules.
Code for saving element :
this.props.itemsStore.push({
text : this.state.text,
done : false,
user : this.props.user.uid
})
Everything ok, but how i can get all records what owns only but authorized user?
This rules doesn't works :
"rules": {
"items" : {
".write" : "auth !== null",
"$item" : {
".read": "data.child('user').val() == auth.uid"
}
}
}
Seems to there no way to get all records only for one user, with security rules, instead of this, i should use something like filter. But again, i don't know how to filter elements in ReactFire, and in manuals no information.
As example how does it work in Parse http://i.stack.imgur.com/l9iXM.png
The Firebase security model has two common pitfalls:
permissions cascade: once you've granted a read or write permission on a specific level, you cannot take this permission away at a lower level
rules are not filters: (this is essentially a consequence of the previous pitfall) you cannot use security rules to return a different subset of children for specific users. Either a user has access to a node, or they don't have access to it.
You seem to be falling for that second pitfall. While the user can access each specific message that they are the user for, they cannot query the higher-level items node since they don't have read access to it.
If you want to secure a list of messages/todos for a specific user, you will need to store that data for that specific user.
items_per_user
$uid
$itemid: true
This is quite common in NoSQL database and is often called denormalizing. See this article called "denormalization is normal" on the Firebase web site. It's a bit outdated as far as the Firebase API goes, but the architectural principles on denormalizing still apply.
To then show the items for a user, you'd do:
ref.child('items_per_user')
.child(ref.getAuth().uid)
.on('child_added', function(snapshot) {
ref.child('items')
.child(itemId.key())
.once('value', function(itemSnapshot) {
console.log(itemSnapshot.val());
});
})
Many developer new to Firebase think that the inner loop will be too slow to load their data. But Firebase is very efficient when it comes to handling multiple requests, since it only opens a connection once per client and pipelines all the requests in the inner loop.
Keep in mind, Rules are not filters. They allow access to nodes based on criteria.
Here's an example simple structure where users 0 and 1 have stored text data within their node.
Data Structure
ToDo
a_user_id_0
text: "some text"
done: yes
a_user_id_1
text: "another text"
done: no
Rules
In this example rule, users can only read/write from nodes that belong to them within the ToDo node, so the path $user_id would be equal to their auth.id. It assumes the users has authenticated as well.
"ToDo": {
"$user_id": {
".read": "auth != null && $user_id == auth.uid",
".write": "auth != null && $user_id == auth.uid"
}
}
If user_0 was auth'd and attempted to read/write data from a_user_id_1 node, it would fail.