Hello I need to configure firestore security roles for my ionic app as shown in here. But when it applies to my firestore it always say permission denied.
If i use just WRITE:TRUE its working.
Here is my firestore db
and Here how i defined security roles according to myone
match /tournaments/{tournamentsID} {
allow read:if isLoggedIn();
allow write:if getUserData().roles.keys().hasAny(['subscriber']);
}
function getUserData(){
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data
}
Now I edited it as
allow write:if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.subscriber == true
Then its working.
But if i try same thing using a function as following
function getRole(role) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles[role]
}
and calling like
allow write:if getRole('subscriber') == true;
Its not working
Related
I would like to know how I can achieve that my user
that has the following fields: uid, friends, notifications, name, username.
Currently my security rules look like this for the user folder
function signedIn() {
return request.auth.uid != null;
}
match /users/{user} {
allow read, update, write: if signedIn();
}
So how can I make a rule for update: "condition",
so that only friends and notifications are updateable,
but not username, uid or name. Any ideas ?
You're looking for map diffs.
For example:
request.resource.data.diff(resource.data).affectedKeys()
.difference("username", "uid", "name"].toSet()).size() === 0;
Or shorter:
!request.resource.data.diff(resource.data).affectedKeys()
.hasAny("username", "uid", "name"];
Also see:
The documentation on map diff operations
The documentation o controlling field access.
I store data in a Cloud Firestore database. Users in my app donĀ“t need to create an account to get data and they can also write data without to login.
Google reminds me every few days that my database is insecure and can be abused by anyone. How can I improve it without accessing Auth variables?
My firebase rules
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Is there a way to make my database more secure without using authentication?
The logic of my app:
My database contains surnames and their origin. If someone enters a name, he gets the origin back from the database. Example: "Doe" -> "Mexican". If the last name does not exist in my database, I call an API and save the value to my database. Every user needs both read and write permission.
What can I do here?
Since the operation that you require writes for is limited (only inserting new items) you have some options:
You could deny writes to end user clients, and instead send a request to a cloud function that does exactly the operation you need (after verifying the input, or any other checks you might want, rate limiting, etc). Cloud functions ignore the security rules as they run with administrative access.
Here is a sample node function that performs a write to a realtime database, and it succeeds when both read and write are false in the security rules (your associated package.json obviously needs to depend on firebase-admin and firebase-functions):
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
let db = admin.firestore();
// This pushes the "text" parameter into the RDB path /messages/(hash)/original
exports.addMessage = functions.https.onRequest(async (req, res) => {
// Grab the text parameter.
const original = req.query.text;
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/messages').push({original: original});
// Respond to the user (could also be a redirect).
res.send('got it: ' + snapshot.ref.toString());
});
You may want to read about how the firebase admin SDK does access control but within a cloud function you should have admin rights by default.
Using the rules language you could only allow create operations. This removes the ability of the client to update or delete existing data. This isn't quite as secure as the prior method, but might be ok for you:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow create;
}
}
}
Also, note this works for firestore (which you are using) but not for realtime database.
Obviously both of these methods could be in some way abused to write lots of data into your database, though the former gives you a lot more control about what is allowed (e.g. you could prevent more than N entries, or more than Y bytes per entry). The later still lets anyone create whatever they want.
The first thing is to start with the documentation. It's strongly recommended that you have an understanding of what rules can do, and translate that into requirements for your app.
What you're describing for your app right now is too vague to come up with good rules. To be honest, without Firebase Authentication, it's not possible to accept writes to a database without Authentication and also avoid abuse, since anyone could write anything from anywhere on the internet. This could also cost you large amounts of money if someone discovers your "open" database.
Check out this documentation. https://firebase.google.com/docs/firestore/security/rules-structure. Configure the writing of unauthenticated users only in the collections you specify.
service cloud.firestore {
match /databases/{database}/documents {
// authentication required
function issignedin() {
return request.auth != null;
}
// authentication not required
function notAuthenticated() {
return request.auth == null;
}
// A read rule can be divided into get and list rules
match /cities/{city} {
// Applies to single document read requests
allow get: if notAuthenticated();
// Applies to queries and collection read requests
allow list: if notAuthenticated();
}
// A write rule can be divided into create, update, and delete rules
match /cities/{city} {
// Applies to writes to nonexistent documents
allow create: if notAuthenticated();
// Applies to writes to existing documents
allow update: if notAuthenticated();
// Applies to delete operations
allow delete: if notAuthenticated();
}
}
}
as a consideration, this will be insecure if the calling API allows indiscriminate writing.
Note: If the API you are referring to is the only one you can write, you must configure only the reading as public
service cloud.firestore {
match /databases/{database}/documents {
// authentication required
function issignedin() {
return request.auth != null;
}
// authentication not required
function notAuthenticated() {
return request.auth == null;
}
// A read rule can be divided into get and list rules
match /cities/{city} {
// Applies to single document read requests
allow get: if notAuthenticated();
// Applies to queries and collection read requests
allow list: if notAuthenticated();
}
// A write rule can be divided into create, update, and delete rules
match /cities/{city} {
// Applies to writes to nonexistent documents
allow create: if issignedin();
// Applies to writes to existing documents
allow update: if issignedin();
// Applies to delete operations
allow delete: if issignedin();
}
}
}
I'm working on a Flutter app using Firebase as a backed. I've set up group based roles in Firebase and the rules simulator in Firebase tells me the user I'm testing has access to the document. When I do a query in my Flutter code, I can see it finds the document and I can see it for a split second before it changes it mind and I get a "Listen for query at students failed: Missing or insufficient permissions." and the document is removed from the snapshot.
The query I use in the Flutter code is as follows:
Firestore.instance.collection('students').where('test', arrayContains: userID).orderBy('name').snapshots()
I have been playing with the document and tried different approaches for the current user to query for the document, and just to test it out I created an array with the userId and look for that.
If I completely skip the rules and just put the "need to be logged in" as requirement then I get a document back but as soon as I use the role based one then it's back to the drawing board. The rules I've set up are:
service cloud.firestore {
match /databases/{database}/documents {
match /students/{student} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
allow read, write : if isOneOfRoles(resource,['teacher', 'student', 'parent']);
}
}
}
Any idea what's causing this?
I'm using Firebase exclusively in my app - Auth, Firestore, Functions and Storage.
Within Firestore, my data is structured as below:
/users/<user_id>/<user_data>
/listings/<listing_id>/<listing_data>
<listing_data> contains a user_id field.
Within Storage, my data is structured as below:
/images/<user_id>/<image_id>/<images>
I have the following scenario:
A user must only be able to access their listings
A user must only be able to download their files
A user can can share their name from their <user_data> with selected users
A user can share selected images with selected users
I have no ideas how I can go about this. At the moment, anyone can access anything if they're authenticated, so I guess the first step is to lock this down, and then some how assign rights?
I thought about adding an access list object, and then writing middleware to check this, but it doesn't feel like the correct way
You have to modify the rules of firestore as:
service cloud.firestore {
match /databases/{database}/documents {
// Restaurants:
// - Authenticated user can read
// - Authenticated user can create/update (for demo)
// - Validate updates
// - Deletes are not allowed
match /restaurants/{restaurantId} {
allow read, create: if request.auth.uid != null;
allow update: if request.auth.uid != null
&& request.resource.data.name == resource.data.name
allow delete: if false;
// Ratings:
// - Authenticated user can read
// - Authenticated user can create if userId matches
// - Deletes and updates are not allowed
match /ratings/{ratingId} {
allow read: if request.auth.uid != null;
allow create: if request.auth.uid != null
&& request.resource.data.userId == request.auth.uid;
allow update, delete: if false;
}
}
}
}
The root of my database is restaurants.You have to replace those parameters with that of yours.
Is there a way to control uploads to a path in Firebase Storage by group?
For instance have an admin group that can upload anywhere or a team that can only upload to a certain path.
After searching around a bit, I didn't find a ready answer, so I'll post what I have so far. It would be nice to know if there are other (better) ways of doing this.
Since I'm trying NOT to use another server, custom authentication tokens are out. However, the request.auth.uid is available to the storage rules. The uid property matches one of the users defined in the Auth area. You'll need to create a function in the storage rules that checks if request.auth.uid is in a group you define.
Firebase storage rules have a unique syntax. It sorta looks like javascript, but it's not. You can define functions, but you can't declare a var within them. Furthermore there is a subset of javascript-like methods available.
For instance, I first unsuccessfully tried the following:
function isAdmin() {
return ["value","list"].indexOf(request.auth.uid) > -1;
}
service firebase.storage {...}
Either the rules editor threw errors when I tried to define a var OR it always returned "unauthorized" when I used .indexOf.
The following ended up working for me.
function isAdmin() {
return request.auth.uid in {
"yaddayadddayaddUserIDKey":"User Name1"
};
}
function isSomeOtherGroup() {
return request.auth.uid in {
"yaddayaddaSomeOtherUID":"User Name2",
"YaddBlahBlahUID":"User Name3"
};
}
service firebase.storage {
match /b/<your bucket here>/o {
match /{allPaths=**} {
allow read, write: if isAdmin();
}
match /path/{allPaths=**} {
allow read, write: if isSomeOtherGroup() || isAdmin();
}
}
}
I don't have enough reputation to comment on #ecalvo's answer. So I am adding an answer. The function isAdmin() can be provided a list as following:
function isAdmin() {
return request.auth !=null && request.auth.uid in [
"uidAdmin1",
"uidAdmin2",
"uidOfOtherAdminsAsCommaSeparatedStrings"
];
}
Rest of the implementation can be borrowed from the answer of #ecalvo. I feel now it is more clear. In the answer of #ecalvo, I was confused why should I give username when I have to compare only uid.