I am developing an application, which uses Firestore as a database. I have a collection of admins, where the id of the documents is the email address of the admin. I want to create a security rule, which enables only admins to create new documents. My current solution looks like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{collectionName}/{document=**} {
allow create: if exists(/databases/$(database)/documents/admins/$(request.auth.email));
}
}
}
But when I try to run the admin app, it gives a missing or insufficient permissions error. Furthermore, when I try to test it in the rules playground, it gives the following error:
Error running simulation — Error: simulator.rules line [6], column [24]. Function not found error: Name: [exists].; Error: Invalid argument provided to call. Function: [exists], Argument: ["||invalid_argument||"]
As far as I understand, somehow the exists function is missing and the document id is invalid, but why? It's just a string, isn't it?
If you are trying to get the email associated with the auth request, you have to do it like this:
$(request.auth.token.email).
You can see details on the structure of the Request.auth object here.
There is no option to make a user Admin and give special privileges in realtime database I think it goes the same to the FireStore.
But what you can do is add a field in the user like userType and give it the value Admin whenever an admin Signs up, subsequently you can create rules based on that.
Related
I'm trying to trigger my function on specific path in storage bucket using:
exports.generateThumbnail = functions.storage.bucket("users").object().onChange(event => {});
When I try to deploy it, console shows:
functions[generateThumbnail]: Deploy Error: Insufficient permissions to (re)configure a trigger (permission denied for bucket users). Please, give owner permissions to the editor role of the bucket and try again.
How can I do that? Do I nedd to setup IAM or bucket permission or maybe something else?
Looks like the issue is that you're trying to reference a bucket named "users" rather than filtering on an object prefix.
What you want is:
exports.generateThumbnail = functions.storage.object().onChange(event => {
if (object.name.match(/users\//)) {
// do whatever you want in the filtered expression!
}
});
Eventually we want to make prefix filtering available so you can do object("users"), but currently you have to filter in your function like above.
When I run a Firebase/Firestore query in app or simulator with a Security Rule using request.response it consistently fails the check. The simulator throws an "Error: simulator.rules line [XX], column [XX]. Property resource is undefined on object."
That would imply the document I'm querying isn't real. It exists in my db, and just in case the simulator doesn't check real documents, I'm making sure to do a sim "create" call for the document immediately before running the get. No actual document ID that I insert seems to be found in this collection or any other for that matter. Clearly overlooking something vital and probably absurdly basic:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /responses/{id} {
allow read:
if request.auth.uid == request.resource.data.userID;
}
}
FYI The query is authenticated, and I'm hitting the path "responses/documentName" which does in fact exist with a "userID" property. Rules that don't use the request.response are working fine.
This is clearly a super boilerplate rule/query. What am I overlooking?
Thanks so much :)
As the error message says, request.resource does not exist. It only exists on writes
Try this instead:
if request.auth.uid == resource.data.userID;
I want to allow links inside my application looking like:
mywebsite.com?u=nc27ri3ucfyinyh3
where nc27ri3ucfyinyh3 is a uuid, so the link can be sent to an anonymous user. The anonymous user should be able to view the page (database read), but it should also log to the database that they've viewed that link (database write).
As we get a warning when our firestore rules look like
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
as it is not secure.
Your security rules are defined as public, so anyone can steal, modify or delete data in your database
How should we handle the case of these anonymous users?
The first thing is that you could write a more restrictive set of rules than you have there (for example, restrict writes to just one collection (by changing the match /{document=**} line to something more restrictive (e.g. just the links collection or something). This, of course, still effectively allows anonymous users the full run of your database, but only within that collection.
Additionally, you can add validation to the incoming request via the request.resource object) -- its likely due to the anonymous nature of the user that you will still have a relatively insecure set of rules.
The data validation approach can look at both the current state of the database (in resource.data) as well as the contents of the incoming request (in request.resource). Here is the reference documentation for Resource and Request objects.
Here is an example rule that assumes these documents:
Exist in the /uuids collection
Are created by some other method (authenticated user, admin API, etc)
Only need to be fetched by ID, not queried as a set.
Only have 2 fields: content and visits
visits must be an integer, and is only allowed to be incremented
When the document is created, visits is initialized to zero.
I have not extensively tested these rules, only used the simulator to confirm they behave roughly as expected, I recommend you write extensive tests for any rules you intend to deploy. In particular, I am not certain about the behavior of the test for only being incremented when the document is under heavy contention.
rules_version = '2';
function notUpdating(field) {
return !(field in request.resource.data)
|| resource.data[field] == request.resource.data[field]
}
service cloud.firestore {
match /databases/{database}/documents {
match /uuids/{uuidValue} {
allow get: if true;
allow update: if (request.resource.data.keys().size() == 2 &&
notUpdating('content') &&
request.resource.data['visits'] == int(request.resource.data['visits']) &&
request.resource.data['visits'] > 0 &&
request.resource.data['visits'] == resource.data['visits'] + 1);
allow write: if false; // these 4 lines can also just be omitted
allow list: if false;
allow delete: if false;
allow create: if false;
}
}
}
This would allow you, for example, to ensure that only exactly the field you want is being touched, and only with valid data (e.g. positive integers or similar).
Remember -- the security rules are your only protection -- users can run arbitrary code against the database within those rules, not just code that you have given them. So, for example, if they can blanket read the collection, they can literally read the entire set of documents in that collection.
Alternatively, it might instead make sense to write an http, https, or callable cloud function that does exactly what you need -- register that the link has been used via a write, and then redirect or serve the necessary data itself. This gives you a lot more control over the specific write, but it does come with some added cost. The advantage here is that you wouldn't need to allow any public or open access to the database at all.
Cloud functions can also be served off of mywebsite.com if that web site is hosted on Firebase Hosting, via rewrite rules.
I have this DB structure in Firebase:
[bets]
-KTd9VKWJwHd_L6j_oAF
date
team1
etc
-KTdCc7uVmueNtcYzU1m
date
team1
etc
[users]
I'm now trying to write Firebase rules to allow users to only read bets assigned to them. But I ran into problems right away. I have these very basic rules:
{
"rules": {
"bets": {
"$bet": {
".read": true,
".write": true
}
}
}
}
When I try to visit /bets (where I display a list of bets) I get thrown the error:
Error: permission_denied at /bets: Client doesn't have permission to access the desired data.
If I instead place the .read and .write under bets instead of $bet, it works fine. What am I missing?
Firebase evaluates read permission at the location where you attach a listener. You attach a listener to /bets, so it rejects the listener since you don't have read permission on /bets.
If you have read permission to /bets, you also can read everything under it. So this means that you can't use Firebase Database security rules to filter data.
See the section rules are not filters in the Firebase documentation. Or search for Firebase questions mentioning "rules are not filters" here and you'll see this is a common pitfall for developers.
i am new to firebase. i have set up a firebase realtime database and can read from and write to it if the read and write rules are set to true.
i have a problem with authentication.i have set up authentication for google and email plus password.
my goal is to allow any user to read the data but only one user (myself) can write data after logging in using a single email address and password.
i can successfully read from and write to the database if i login with google (with rules set to: auth != null.)
i can also read from and write to the database using the same rules (auth != null) if i log in with the email address and password.
i don't know how to set it up to only allow write access for the single user logging in with an email address and password.
i have tried including a user node in the rules but i can't get access when using the simulator (see below) and i don't know how to include the uid (which i can get after logging in) when building the reference - this is the reference i currently use (which works with the read and write rules set to true):
DatabaseReference databaseReference = mRootReference.child("Action_helper/1/Action_table_of_contents");
i have not included a users node in my database as i am assuming that is taken care of by firebase authentication.
here is the layout of my data:
i have tried the simulator using various rules options. testing access using these settings in simulator (choosing the Custom provider option):
Auth {"provider" : "firebase", "uid" : "Rp3OgoaABMN3hqTv0aF29ECQRCQ2"}
note: i get the provider and uid from Firebase object after logging in with an email address and password which i have set up in Firebase authentication:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// User is signed in
userId = user.getUid();
String provider = user.getProviderId();
i would appreciate some help in 1) formulating my rules, 2) if and how i should change my data structure, and finally 3) how to include the uid in the database reference which i'll use to write data to the database.
thanks
There is no users node so, defining in rules would not help. I think the rule that may work would be something like below (assuming 0 and 1 are uid):
{
"rules": {
"Action_helper":{
"$uid":{
//user-based security
".read": "auth != null && $uid === auth.uid",
".write": "auth != null",
}//$uid
}//Action_helper
}// rules
}
Examining above rules by default if we do not define rules then it is false i.e. at Action_helper it is false for both read and write. When it comes to the node uid (where $ denotes wild card) then, we check if the user id of logged in user is same to uid of this node and define rules accordingly.
I highly recommend to go
through the link The key to Firebase security - Google I/O 2016 , it is very helpful, easy to follow, and best explanation I found so far with demo example.
The data layout will depend on your requirement and screens. Although Firebase allows 32 level of nesting it is highly recommended to nest nodes as less as possible. And other important thing to think about the data layout is to keep data as denormalize as possible even if we
have to make copies of fields across the nodes.
To include uid in database reference you can keep on appending each child:
DatabaseReference databaseReference = mRootReference.child("Action_helper).child(uid).child("Action_table_of_contents");
So, here we are referring from root node to child "Action_helper" and going further down to it's child that matches uid and of that uid we are referencing to the child "Action_table_of_contents".
thanks for the help. i managed to get it working (partly) but am not sure that i am doing it correctly. here is my data structure (i have changed the names)- there is one user node (using the authentication uid), and two child nodes which contain the data:
and here are my rules:
essentially it works in the simulator but in code, i am able to log in and read and write. BUT i now have a problem, if i don't log in then the uid passed in the query reference is null, if i put a dummy value as the uid then i can't access the data at all (as the data is under users/the_valid_uid node and the dummy uid does not match the_valid_uid).
so how do i build a database reference without hard coding the valid user's uid? so that i can access the data in the Addiction_items and table_of_contents_items nodes (my aim is to allow anyone to read data in both nodes but to only allow one user (myself) to be able to write to both nodes after logging in with my email address and password?
thanks