I'm having some problem with the READ rules of Firestore currently
Here is my data structure
{
email: example#gmail.com,
username: geekGi3L,
birthday: 1995/02/14,
photo: <firestore-download-url>
}
The rules currently I set is
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user} {
allow read;
allow write: if request.auth.uid != null && request.auth.uid == user;
}
}
}
How could I set the rules to allow user to READ the specific fields like email and birthday only if request.auth.uid != null && request.auth.uid == uid while username and photo should be readable by every user?
Thank you <3
In Firstore, there is no per-field access control for reading fields of a document. The most granular unit of access is the document. A user either has full access to read a document in its entirety, or they don't have any access at all.
If you need to change access per field, you'll have to split the fields of the document into multiple collections, with each collection having access control appropriate for the fields of the documents within. It's very common to have a split between public and private data like this.
Related
I have Security rules like below in my Firestore database
just to be sure I want to ask here for best practice of security rules firestore
So I have collection of userData and communityPost
user data only can be access by content owner that create it (content owner can create and update it)
for community post I want everyone auth and non-auth user can read the data (public)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow only authenticated content owners access
match /userdata/{document} {
allow read, write: if request.auth != null && request.auth.uid == userId
}
match /communityPost/{document} {
allow read: if true;
}
}
}
is this correct rules ?? this is my first time dealing with collection based rules,
thanks for your time
You can check the properties within the document
match /userdata/{document} {
allow read, write: if request.auth != null && request.auth.uid == resource.data.owner
}
or you can match the document id to the user
You can check the properties within the document, notice the match path
match /userdata/{userID} {
allow read, write: if request.auth != null && request.auth.uid == userID
}
As for making it public, Firestore discourages purely open database so you need a few conditional statements
You can check the properties within the document
match /communityPost/{document} {
allow read: if resource.data.public == true;
}
I am currently working on a a app and in that user needs to make a new account. Your Enters first name and last name then the app automatically suggest a username which is unique and it will be the document name of that user. I had set the firestore secutity rules as follows,
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
after user enters the username it checks that the username used or not before move to the next screen.
Future<bool> checkUsernameExist(String name)async{
bool usernameExistSate;
await firestore.collection('users').doc(name).get().then((docSnapShot){
if(docSnapShot.exists){
usernameExistSate = true;
}else{
usernameExistSate = false;
}
});
return usernameExistSate;
}
Currently above system works fine without any problem. But I have a problem, With the firebase security rules sets to below condition how users able to read the documents to check the similar document names are present?
allow read, write: if request.auth != null;
First, I would not use the usernames to store your data in firestore but the uid provided when you are authenicated with google auth. This will allow you much safer access to the database with security rules like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
For your second problem I would just create a second collection in the root of the firebase project named for example usernames with all usernames stored in a big list so you can query them safely via the firebase API. For that to be possible you have to give the authenticated device access to this collection too via for example adding this under
match /users/...
match /usernames/{document=**} {
allow read, write, update, delete, create: if request.auth != null;
}
Of course then, you have to keep track of both lists when making changes. But this way an authenticated user has only access to his data and all usernames in the worst case.
I have a problem of understanding with the security rules of cloud firestore. I don't understand how to check a user's uid with a map where they can access their data.
Thank you in advance for your answers
Here is the rules i have tried
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
//TEST ONLY
//match /{document=**} {
// allow read, write;
//}
// match logged in user doc in users collection
match /users/users/{userId} {
allow create : if request.auth.uid != null;
allow read : if request.auth.uid == userId;
match /{anything=**} {
allow read, write : if request.auth.uid == userId;
}
}
}
}
And here an exemple of how i use Firestore :
// Collection reference
final CollectionReference _usersCollection = Firestore.instance.collection('users');
Stream<List<double>> get existingRecord {
return _usersCollection.document('users').snapshots()
.map(_existingRecordListFormSnapshot);
}
List<double> _existingRecordListFormSnapshot(DocumentSnapshot snapshot) {
...
double tips = snapshot.data['$uid']['data']['$year']['$month']['$week']['$day']['Tips'];
}
My problem is that I want only the user to have access to their data. For this I made a 'users' document which contains a 'users' map which contains all user data, named with their unique uid. And I am unable to set up the security rules so that only the user has access to his data.
The diagram looks like this:
users / users / [{userID}, {userID}, ...]
I don't know if I'm clear but I really can't find how to only allow the user to access their data in the users data map
Dont put all the users in the same document, instead you should have one document per user, (with the document's name equal to the Firestore uid for simpler rules).
Then your rule can simply be:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// match logged in user doc in users collection
match /users/{userId} {
allow create : if request.auth.uid != null;
allow read : if request.auth.uid == userId;
match /{anything=**} {
allow read, write : if request.auth.uid == userId;
}
}
}
}
I have a collection of data, say, products and two different set of users merchants and users. How do I grant read write access to merchants on products and only read access to users?
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
The above rule only defines access for a single users.
Starting point:
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /products/{productid} {
// Anyone authenticated can read
allow read: if request.auth.uid != null;
// Only merchants can write
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.type == 'merchant'
}
Assuming users have field type with possible values ['user', 'merchant']
More info here
As I understand it, Firestore does not allow queries within fields of type array, which is a shame. Therefore, if you want to be able to query the contents of an array you have to set up a field as an object type and then set the fields like a map, this is called a nested map. I want to have a map where the key is the ID of another user. Therefore, the database structure is:
database
users
{userId}
friends
userId1: true
userId2: true
The 'userId1' and 'userId2' field names will vary depending on the userId of the person listed as a friend.
The question is, how do I write my security rule so I can find my documents (via my {userId}) and the documents of other users where my {userId} is a field in the 'friends' object of the other user's document?
I guess it needs to be something like..
match /users/{userId} {
allow read, update, delete: if resource.data.userId == request.auth.uid;
allow read: if resource.data.friends.{userId} == true;
}
But of course this does not work because you cannot seem to use the variable {userId} to name the field that you want to perform a test on. So, if this cannot be done, what is a way to search for documents and have my {userId} stored somehow in someone else's document?
Edit
Well, I think I have the rules determined (see below). However, when trying to test these rules I can't seem to write a Swift call to retrieve data based on that friends object. My Swift call is:
db.collection("users").whereField(FieldPath(["friends.\(userId)"]), isEqualTo: true)
So, my questions are:
Are the rules below correct?
How do I make a Swift call to find the people with a certain userId in the field name of an object type?
service cloud.firestore {
match /databases/{database}/documents {
match /users/{documentId} {
allow read, write: if isOwner();
allow read: if getFriend(request.auth.uid) == true;
function isOwner() {
return request.auth.uid == resource.auth.uid;
}
function getFriend(userId) {
return getUserData().friends[userId]
}
function getUserData() {
return get(/databases/$(database)/documents/rooms/{documentId}).data
}
}
}
}
I still have not resolved the problem of accessing fields in an object, but it is noted that my Security Rules where generally invalid. You cannot have multiple lines with the same rule type in it, you cannot have multiple lines with 'allow: read' for example. You must use && and ||. For example, the correct definition for the basic rules if you want to check two things are:
// Database rules
service cloud.firestore {
// Any Cloud Firestore database in the project.
match /databases/{database}/documents {
// Handle users
match /users/{documentId} {
// The owner can do anything, you can access public data
allow read: if (isOwner() && isEmailVerified()) || isPublic();
allow write: if isOwner() && isEmailVerified();
// Functions //
function isSignedIn() {
return request.auth != null;
}
function isOwner() {
return request.auth.uid == resource.data.userId;
}
function isPublic() {
return resource.data.userId == "public";
}
function isEmailVerified() {
return request.auth.token.email_verified
}
}
}
}