I'm trying to implement a Firebase Security Rule to give users access to a "Project" document.
I have a collection of projects and each project has a members collection with the UserIDs.
The User is authenticated.
My structure looks like this :
Structure
This is my Security Rule, but it's not working.
Can anyone help me ? What am I doing wrong ?
//Firebase Rule
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /projects/{projectID} {
allow read, get, write: if request.auth.uid != null && exists(/databases/$(database)/documents/projects/{project}/members/$(request.auth.uid));
}
}
}
//Flutter Call
Stream<List<ProjectModel>> getUserList() {
print("getUSer");
return FirebaseFirestore.instance.collection('projects').snapshots().map(
(snapShot) => snapShot.docs
.map((document) => ProjectModel(
name: document.data()['name'],
owner: document.data()['owner'],
description: document.data()['description']))
.toList());
}
Try the following Example :
Authenticated User that can read or write to Projects Collection :
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /projects/{projectID} {
allow read, write: if request.auth != null && exists(/databases/$(database)/documents/projects/{projectID}/members/$(request.auth.uid));
}
}
}
Note : Inside Your Members Collection The Documents inside it must be Auth .uid Like the following Photo
Related
I'm trying to restrict access to a collection based on a users "role" on the document as well as whether they're signed in.
Just checking if they are signed in works:
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isSignedIn();
}
}
}
But I want to make it a bit more granular, I'm following this Google guide. When I configure it exactly the same my frontend client errors with FirebaseError: Missing or insufficient permissions. However, testing the rule in the Firebase portal works(?).
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.access[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isOneOfRoles(resource, ["owner", "viewer"]);
}
}
}
As mentioned, testing the rule via Firebase works, however when querying from my frontend app it fails.
// VueJS
let workspace = []
async fetchWorkspaces() {
const db = firebase.firestore();
await db.collection('workspaces').get().then(response => {
response.forEach(snapshot => {
this.workspaces.push(snapshot.data());
})
})
}
Database document
I've also tried storing the RBAC for each user as a document in a subcollection and using the following rule
allow read: if isSignedIn() && exists(/databases/$(database)/documents/workspaces/$(workspace)/users/$(request.auth.uid));
Still doesn't work. I can only seem to grant broad access (is signed in)
I am trying to make a web app that displays different elements based on what permissions I give to a user.
All the permissions are stored in the the Cloud Firestore database at /users/{userId} in the field "permissions", which is an array containing the permissionId's.
In /photo_libraries/{libraryId} I have a field called permissionId, which is a string.
I now want to give users that have the right permissionId to be able to read the document in /photo_libraries/{libraryId} that has that permissionId.
I've tried this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /photo_libraries/{libraryId} {
allow read: if get(/database/$(database)/documents/photo_libraries/$(libraryId)).data.permissionId in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.permissions;
}
}
}
But this doesn't seem to work, I'm quite new to the Firestore rules. Can anyone help me out?
P.S. This is how my database looks like:
This is the code I try to run:
const db = firebase.firestore(); const auth = firebase.auth();
auth.onAuthStateChanged(user => {
if (user) {
db.collection('photo_libraries').get().then(snapshot => {
// set up the UI
}, err => {
console.log(err.message);
});
} else {
// Logging out stuff
};
});
In the console I get the error message:
Missing or insufficient permissions.
Thank you,
Jonas
Try this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /photo_libraries/{libraryId} {
allow read: if resource.data.permissionId in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.permissions;
}
}
}
But a better solution would be to add the persmissions array as a custom claim, then you dont need to call get.
When querying Firestore make sure you are only querying the documents you can actually access. Look for "rules are not filter" on google and you will get plenty of hits on SO and in the official Firebase documentation.
Firebase keep telling me
We've detected the following issue(s) with your security rules:
any user can read your entire database
I have changed the rules but that rules doesn’t work in my app because all user can read from db and only authenticate user can write to db.
Firebase says that write and read should be performed until we login. But in my case every user can read and only login user can write.
Any ideas how to solve this ? or I'm I doing it wrong ?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if request.auth != null;
}
}
}
Can you set your read to false explicitly?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if false;
allow write: if request.auth != null;
}
}
}
That should do it. Let me know if it persists.
The root cause is that even though you are allowing only authenticated users to read or write but they have access to the whole database as mentioned in the Google Cloud Firestore Documentation. That also means any authenticated user can write anything in your database.
If you database has a separate document for each user, I would suggest using the following rules which allows users to write/read their own data only.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.uid === userId;
}
}
}
here I have a little problem concerning the firestore security rules
I create an online notepad and I would like these notes to be accessible only by certain users identified by a groupId. I specify that I have specified a groupId to my cities also in order to be able to match my identified users to my identified cities.
In terms of my code, everything is fine but it is at the level of the firestore rules that I do not succeed.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users /{documents = **} {
allow create,read,write : if request.auth != null
}
match /cities/{city}/citees/{citee}{
allow read : if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.groupId == get(/databases/$(database)/documents/cities/$(city).data.groupId
}
}
}
There is a syntax error in it. Seems to be missing ) after $(city). It should be like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users /{documents = **} {
allow create,read,write : if request.auth != null
}
match /cities/{city}/citees/{citee}{
allow read : if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.groupId == get(/databases/$(database)/documents/cities/$(city)).data.groupId
}
}
}
yes, I have access to the database because when I try to recover my "uers" collection alone, I can do it
I have a working read rule in fireStore to check that a user is in the users array of the /accounts/{account} resource:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /accounts/{account} {
allow read: if request.auth != null && request.auth.token.email in resource.data.users;
}
}
}
In order to simplify the code and to use a function elsewhere to check user access based on a resource id, and following the information in this link: Security Rules! 🔑 | Get to know Cloud Firestore #6 19:25 I have attempted to abstract the code:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function hasUserAccess(account){
return request.auth.token.email in get(/databases/$(database)/documents/accounts/$(account)).data.users;
}
match /accounts/{account} {
allow read: if request.auth != null && hasUserAccess(account);
}
}
}
So that when I want to cross-reference the access for related documents, I can call the function. Why does the abstracted version fail to work? It seems like it should be working correctly based on the youtube video.
You will need to use hasAny() to check if the users list of accounts contains the email passed.
Also note that you have to pass the request.auth.token.email in the function.
Please use the following code in order to see if the user exists in the field "users" of your other collection:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function hasUserAccess(account,email){
return get(/databases/$(database)/documents/accounts/$(account)).data.users.hasAny([email]);
}
match /accounts/{account} {
allow read: if request.auth != null && hasUserAccess(account, request.auth.token.email);
}
}
}
This works, it seems I couldn't use the function within the collection the function was inspecting?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function hasUserAccess(account){
return request.auth.token.email in get(/databases/$(database)/documents/accounts/$(account)).data.users;
}
// You can't use the function within this match condition
match /accounts/{account} {
allow read: if request.auth != null && request.auth.token.email in resource.data.users;
}
// You CAN use the function within this match condition as it isn't for the collection /accounts
match /otherData/{account} {
allow read: if request.auth != null && hasUserAccess(account)
}
}
}