I'm new to Firestore Rules. All I want is simply check if user id exists within the object. Im reading data using FirestoreRecyclerAdapter like so (query):
Query query = FirebaseFirestore.getInstance()
.collection(EVENTS_INFO)
.whereEqualTo("users." + user.getUid(), true);
Here is how structure looks in Firebase Console (screenshot):
Firestore rules here:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth.uid != null;
}
match /events/{event} {
function isUser() {
return isSignedIn() && resourse.data.users[request.auth.uid] == true;
}
allow read: if isUser();
}
}
As there is no simulator I cant fast get the issue. allow read: if true works as desired. Any clues?
There is an issue with this line...
resourse.data.users[request.auth.uid] == true;
resourse should be written resource
Related
I have a collection in which I am storing user requests in documents having documents ID as user's email. In the document, I am creating fields the key for which is being generated at client side.
Now, the problem that I am facing is that user can overwrite the existing field/request in the document if the key matches which I don't want to happen.
What I tried was to use this rule which unfortunately does not work
resource.data.keys().hasAny(request.resource.data.key();
So how can I achieve this?
Below are the screen shot of the firestore data and the current security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /roles/{userId}{
allow read: if isSignedIn() && hasId(userId);
}
match /requests/{email} {
allow read, update: if isSignedIn() && hasMail(email)
}
//functions//
function hasMail (email) {
return request.auth.token.email == email;
}
function hasId (userId) {
return request.auth.uid == userId;
}
function isSignedIn () {
return request.auth != null;
}
function getUserRole () {
return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role
}
}
}
You can check if a resource already exists. Here an example:
allow write: if resource == null // Can create, not update
Use that to restrict any edit or update of the data. If you have additional rules you can granulate them to update, delete and create.
I am trying to setup some basic Firestore security rules in my database. I am having some trouble finding the relevant documentation to learn how to do this.
Currently my document is structured like this:
project(document): {
createdBy(string): chris#emailaddress.com,
users(object): {
{ graham#emailaddress.com(object): access: write },
{ paul#emailaddress.com(object): access: read }
}
}
I'd like to setup my rules so that:
Users must be signed in to read, write or delete anything
If a user is added to a project with 'read' access they can only read the document.
If a user is setup with write access they can update and read the document but not update the createdBy field.
If a user has created the document they can read, update and delete the document.
My security rules are setup like this:
service cloud.firestore {
match /databases/{database}/documents {
match /projects/{projectId} {
allow read: if existingData().users[getUser()token.email].access != null && isSignedInAndVerified()
allow read, update: if existingData().users[getUser()token.email].access != "write" && isSignedInAndVerified()
allow update, delete: if sameAsEmail(existingData().createdBy) && isSignedInAndVerified()
}
//my functions
function getUser(){
return request.auth
}
function existingData(){
return resource.data
}
function sameAsEmail(resource){
return resource == request.auth.token.email
}
function isSignedInAndVerified() {
return request.auth != null && request.auth.token.email_verified;
}
}
}
Incorrect use of syntax use: getUser().token.email instead.
I have the following firestore rules setup where I want to let anyone create a document in the suggestions collection, but only a certain user can update it. The rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}
When I test this in the provided simulator, it works fine; however after waiting 30 minutes I test in my deployed app and I get the error:
Error: Missing or insufficient permissions.
In my app I'm doing an add() call on the suggestions collection. So in my rules where I specify allow create, that should be sufficient. I added read in case the returned document would count as a read.
Client: (AngularFire)
this.afs.collection('suggestions').add(sugg).then(() => {
this.submitted = true;
}, err => console.error('Firebase error:', err));
The issue was that Firestore couldn't try to match a uid when request.auth was null. The solution was to add some functions:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isAdmin() {
return isSignedIn() && request.auth.uid == 'abc123';
}
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}
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;
}
}
}
The solution is in the end of the post. Check it out.
Решение проблемы в конце поста. Дочитайте.
just a simple question: whats wrong with this and why this is not working?
Trying to get access with user who has role 'admin' in users section to the /titles/{anyTitle} but still get
Missing or insufficient permissions.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false;
allow read: if false;
}
function userCanWrite () {
return get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";
}
match /titles/{anyTitle=**} {
allow read: if request.auth != null;
allow write: if userCanWrite();
}
}
}
Here is my database structure
P.S.
I tried another rule from official documents
get(/databases/{database}/documents/users/$(request.auth.uid)).data.isAdmin == true;
and this is not working too
UPDATE: CORRECT WAY TO DO IT
Support helped me find the solution
this is how you should do:
db structure:
users -> {{ userid }} -> { role: "admin" }
database rule settings:
get(usersPath/$(request.auth.uid)).role == "admin" || get(usersPath/$(request.auth.uid)).data.role == "admin";
I contacted to the Firebase support to report that bug and they gave me a temporary solution on this. It seems that they are having a bug in their systems on the security rules side. They say that the documentation is ok, but for now we should workaround this way:
get(path).data.field == true || get(path).field == true;
Because the bug is that data object isn't populated, you should check both properties. There's no ETA for launching a solution on this bug, so I asked they if they could give me an advice when they solved this issue, so I'll keep this answer up-to-date with their information.
So the way I've solved it is I've created another Collection Called admins
Then I've just added the uid of the user I needed there as such -
Here is my database structure - https://i.imgur.com/RFxrKYT.png
And here is the rules
service cloud.firestore {
match /databases/{database}/documents {
function isAdmin() {
return exists(/databases/$(database)/documents/admins/$(request.auth.uid));
}
match /tasks/{anyTask} {
allow read: if request.auth != null;
allow create: if request.auth != null;
allow update: if request.auth != null && isAdmin();
allow delete: if request.auth != null && isAdmin();
}
}
}
You can view my full Open Source project here:
https://github.com/metaburn/doocrate
You should use $(database) instead of {database} in your code:
get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";
What worked for me was moving the userCanWrite function above my rules. It appears that the function has to be defined before any of the match rules that call it. Maddening :-)
This is the Firestore rule I use to check if the user is admin.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if userIsAdmin();
}
function userIsAdmin() {
return getUserData().userRole == 'Admin';
}
function getUserData() {
return get(/databases/$(database)/documents/User/$(request.auth.uid)).data;
}
}
}