How to check firestore rule for group membership - google-cloud-firestore

I want to limit access to an entire app to a google group.
So my firestore rule could look like
function isGoodEmail() {
return request.auth != null &&
request.auth.uid != null &&
request.auth.token.email.matches('.*#example[.]com$') &&
request.auth.token.email_verified;
}
function isAllowedUser() {
return isGoodEmail() && request.auth.token.email in [
"user1#example.com",
"user2#example.com"
];
}
match /{document=**} {
allow read, write: if isAllowedUser();
}
}
However, I don't really want to hardcode the list in the rules file, because it's also used elsewhere (e.g. on the homepage to show unauthorized user some special home page). I would like a condition like:
request.auth.token.uid in "mygroup#example.com"
Does firestore have any such provision or am I SOL ?

Firebase security rules don't know anything at all about Google Groups. All you have really access to is an email address. If you don't want to hard code them, you could store them individually in documents and use a query to figure out if the email address exists. But you would have to keep the database in sync with the group somehow.

Related

In firestore rules, allow reading only users that belong to at least one of the workspaces to which the client belongs

According to the program architecture, the "member" object has a unique pair of workspaceId and userId. Thus, each workspace has one or more members (users), while each user has one or more members (workspaces).
To prevent the client from reading users that do not belong to any of the workspaces s/he belongs to, I came up with the following firestore rule:
match /users/{userId} {
allow read: if request.auth != null && belongsToAnyWorkspace(userId);
}
function belongsToAnyWorkspace(userId) {
let memberWithUser = get(/databases/$(database)/documents/members/{document=**}).data;
return memberWithUser.userId == userId && existAuth(memberWithUser)
}
function existAuth(memWithUser){
let member = get(/databases/$(database)/documents/members/{document=**}).data;
return member.workspaceId == memWithUser.workspaceId
&& member.userId == request.auth.uid;
}
The result is that firestore blocks reading for all users. My impression is that the logic of my formulas is correct, but the syntax is flawed. For example, I don't think it's correct to use there wildcard {document=**} as a way to get "any document from the collection members". Any help is appreciated!

FireStore Security Rules for Content Owner only and Public

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;
}

Why firebase_firestore security rules not worked for document read

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.

How to read/write specific data on Firestore

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.

Firestore: How to set security on object types with users as field names

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
}
}
}
}