Don't allow deletions on Firebase storage? - firebase

If I wanted to not allow users to delete a file stored in Firebase storage, what rule would I need to write to accomplish this?
I know for Firebase database I would do something like:
".write": "newData.val() != null"
But how would I do this for storage?

Pretty sure this has been answered a few times (in a few ways), but the easiest answer I've seen is:
allow write: request.resource.someProperty == resource.someProperty || resource == null;
someProperty can be a hash (if you don't want to allow overwrites) or a name (if you want the contents to be overwritten by a new object).

One way to do this would be to only allow writes if the MD5 hash of the new file is the same as the existing file:
// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.md5Hash == resource.md5Hash;
There are probably more/easier ways. But this is the first one I came across in https://firebase.google.com/docs/reference/security/storage/.

Related

Firebase how check path contains value and allow read and write other path

At the moment i try to build a chat in flutter with google firebase.
Now i would like my database more secure.
That means only users (room_3) in chat can read and write data data.
Is it possible to check a path contains a user value
and if the user value is contains allow read a other path?
Here my database structer:
/chat/product_id/product_id_12345/chat_room_id/room_1/message_1/message2...
My idea is i add in 'room_1' the user id.
Then i check the user is contains in 'room_1'.
If the user is contains i allow read and write data the complete path (message_1/message_2...).
Here my example:
If you have any questions fell free to ask me.
Many thx.
In the security rules for the messages subcollection you can read the parent document and check whether the current user is in the user_id field with:
...
match /messages/{message} {
allow read:
if request.auth != null &&
request.auth.uid in
get(/databases/$(database)/documents/chat_room/$(chat_room_id)).data.user_id
}

Firestore security rule that only allows empty documents

I'm basically trying to use a firestore collection as a an email list. Anyone can create a document that has their email as the id and nothing more. The tricky part is the "and nothing more" bit. When no data is provided in the request, request.resource is undefined which you can't check for in security rules to my knowledge. Is this possible? Or is it necessary to have something like one mandatory field for this use case?
Having empty documents regularly leads to issues down the line. Why not require a single marker field, and validate that in rules?
request.resource.data.keys.hasOnly("marker")
For the benefit of others looking to make an email list in firestore, this is the full rule I ended up using:
match /email-list/{email} {
allow get: if true;
allow list: if false;
allow create: if request.resource.data.keys().hasOnly(["marker"])
&& request.resource.data.marker == true
}

Firestore rules: Prevent overwrite of property

In Firestor rules, how can I allow new documents to be created to a collection, and new values to be added to a document but not values in the document to be overwritten?
Please see the following for a clearer understanding:
Collections -------------- Documents -------------- Values
Users -------------------- DonutCoder -------------- isAdmin, birthDate
Now, if I want to add email to the values under the document Donut Coder how can I do this but prevent overwriting (eg. changing isAdmin to true so the user gets more priveleges)
What I have tried: allow read, create - but this allows new documents to be created but no properties in the document to be changed.
1) Don't add any code that overwrites the same path.
2) TO prevent others from reverse-engineering and update it, add this:
allow update: if false
Again anyone can still attempt to create the same document again so you need to add more security rules to it. Do check the official documentation for that.
I have answers regarding similar questions but they refer to realtime database. Though you can check it here.
The allow update: if false just prevent updating the existing value.
This official documentation page seems to show how to do this: https://firebase.google.com/docs/firestore/security/rules-fields#preventing_some_fields_from_being_changed
allow update: if (!request.resource.data.diff(resource.data).affectedKeys().hasAny(['average_score', 'rating_count']));
Preventing isAdmin or birthDate being updated:
allow update: if request.resource.data.isAdmin = resource.data.isAdmin
&& request.resource.data.birthDate = resource.data.birthDate;
You can start building on this depending on your needs. For instance, if you want to allow only logged in user and modifying only one field at a time:
allow update: if request.auth.uid != null
&& request.resource.data.diff(resource.data).affectedKeys().size() == 1
&& request.resource.data.isAdmin = resource.data.isAdmin
&& request.resource.data.birthDate = resource.data.birthDate;

Check if Storage Resource Exists

I am wondering if the following will cover all cases where there was no resource at the request's path before:
allow write: if resource == null;
This should only allow files to be created but not updated.
Yes - that is all you need to prevent editing/overwriting files. Note that it will also prevent any deletions, if you want to allow deletions you'd need to add in a condition something like:
allow write: if (resource == null || request.resource == null);
I'm sure you've already found the official docs (subsection Resource Evaluation) on this stuff... but I'll include a link here to an answer by Google's own Frank van Puffelen that acts as a good reference.

Firebase database rules – `data.exists()` always seems to be true, possible bug?

I am trying to secure my firebase database to allow the creation of new records, but not allow the deletion of existing records. Ultimately, I plan to utilise Firebase authentication in my app as well, and allow users to update existing records if they are the author, but I am trying to get the simple case working first.
However! No matter what I try in the database rules simulator, despite what the documentation seems to suggest, the value of data.exists() seems to always be true. From what I what I can understand from the documentation, the variable data represents a record in the database as it did before an operation took-place. That is to say, for creates, data would not exist, and for updates/deletes, data would refer to a real record that exists in the database. This does not seem to be the case, to the point where I am actually suspecting a bug in Firebase, as when setting the following rules on my database, all write operations are disallowed:
{
"rules": {
".read": true,
".write": "!data.exists()"
}
}
No matter what values I put into the simulator, be it Location or Data. I have even written a small EmberJS app to verify if the Simulator is telling the truth and it too, is denied permission for all write operations.
I really have no idea where to go from here as I am pretty much out of things to try. I tried deleting all records from my database, which lets the simulator think it can perform write operations, but my test app still gets PERMISSION_DENIED, so I don't know what's causing inconsistencies there.
Is my understanding of the predefined data variable correct? If so, why can't I write the rules I want? I have seen snippets literally trying to achieve my "create only, no-delete" rule that seem to line up with my understanding.
Last note: I am trying this in a totally new Firebase project with JUST the rules above, and only ~a few records of junk data laying around my database.
Because you have placed the !data.exists() at the root location of your database, data refers to the entire database. You will only be able to write to the database when it is completely empty.
You indicate that you run your tests with only a few records of junk data laying around my database. Those records will cause data.exists() to be true.
You can achieve your goal by placing the !data.exists() rule in your tree at the specific location where you want to require that no data already exists. This is typically done at a location with a wildcard key, as in the example you linked:
{
"rules": {
// default rules are false if not specified
"posts": {
".read": true, // everyone can read all posts
"$postId": {
// a new post can be created if it does not exist
// existing posts can only be edited by their original "author"
".write": "!data.exists() && newData.exists() || data.child('author').val() == auth.uid",
".validate": "newData.hasChildren(['title', 'author', 'timestamp'])",
}
}
}
}

Resources