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.
Related
I use Firebase's Firestore. I have a collection called "root" which has an item called "db".
I want to allow read and write access to everybody on exactly that item.
This is my Rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if
request.path == 'root/db';
}
}
}
This rule was there by default, except this line request.path == 'root/db'; which is my custom. I tried to set a timestamp rule and it works. But this by path does not work.
This is my request from the browser:
const ref = doc(dbApi, "root", "db");
console.log(ref.path);
let db: Database;
try {
db = (await getDoc(ref)).data() as Database;
} catch (err) {
console.log(err);
throw err;
}
ref.path logs root/db and err logs FirebaseError: Missing or insufficient permissions.
(Using "firebase": "9.6.4" javascript package in the app)
I've also tried some variations on this https://firebase.google.com/docs/reference/security/storage#path
Rather than use the catch all (/{document=**}) and then filter, just filter at the match statement instead:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /root/db { <-- change it here
allow read, write: if true;
}
}
}
While this works, you should further restrict the rules to limit the shape of this document. If your database is all based on a single document, consider the RTDB instead as it provides finer grain controls over the data in the database.
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'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
I want to make it so I can have authenticated users write to a collection that is only read/write/create-able to that user only. I'm struggling with the most basic Firestore setup. My firestore rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /{userId} {
allow read, write, create: if request.auth.uid == userId;
}
}
}
I'm assuming this will prevent read/writes/creates to the database unless the initial part of the path matches the UID of the logged in user.
My JavaScript code looks like this:
function addSomeData(data) {
console.log( "Sending data with: ", user.uid, data );
db.collection(user.uid).add({ data })
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
}
I definitely have user.id set correctly after successful login.
Using things this way always gives me this error in the console:
Error adding document: Error: Missing or insufficient permissions.
If I revert to the original rules like this then the document is successfully created:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
What I'm trying to do is have each collection start with the UID of the logged in user. So, a sample shape of the data might be this:
/ax323/brty/data="Hello"
/ax323/98da/data="Goodbye"
/br981/ha31/data="No comment"
So, ax323 is a UID in Firebase, as is br981. ax323 has two documents in the collection, while br981 has one.
Is the "shape" of my data the problem?
I don't really understand what the {document=**} means in the original rules, and whether I need to convert my authentication rule to something similar.
In your database rules you have used match /users/{userId} {
This rule will apply only to the document mathching that particular path.
So if your document path is /ax323/brty/data then your rules should be like
service cloud.firestore {
match /databases/{database}/documents {
match /{userId}/{document=**} {
allow read, write, create: if request.auth.uid == userId;
}
}
}
Also looking at your question, I can't get what brty means when you mentioned
/ax323/brty/data="Hello" ?
I'm self studying firestore and I could not figure out a way to only allow a user to update, delete or read only the collections added by them.
This is the structure I'm using:
I use firebase auth for user handing. I save the currentUser.uid as user_id in the database for each collection.
These are the rules I'm using
service cloud.firestore {
match /databases/{database}/documents {
match /tasks{
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
When I try to read/get the data I get Missing or insufficient permissions error.
I'm using the web api (JavaScript) for firestore. This is the code I'm using to read data.
function read() {
db.collection("tasks").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
the error was in my JavaScript I was getting all without filtering by user
function read() {
let taskColletion = db.collection("tasks");
taskColletion.where("user_id", "==", firebase.auth().currentUser.uid).get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
This is actually explained on the Firestore Documentation(I recommend reading it).
You're missing a wildcard after /tasks:
service cloud.firestore {
match /databases/{database}/documents {
match /tasks/{task} {
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
}