I have the following rule set on my firestore:
match /names/{nameId} {
allow read: if request.auth.uid != null;
allow update: if request.auth.token.creator == true && request.writeFields.size() == 1 && ('images' in request.writeFields);
allow create: if request.auth.token.creator == true;
}
However, when I'm trying to create a new object using java(kotlin) client:
val data = hashMapOf(
"name" to name
)
mFirestore.collection(COLLECTION_USERS).add(data)
.addOnSuccessListener { doc ->
Log.i(TAG, "On New Document: " + doc.id)
}.addOnFailureListener { ex ->
Log.w(TAG, "Failed adding document: ", ex)
}
If I change the rules to make update same as create:
match /names/{nameId} {
allow read: if request.auth.uid != null;
allow update: if request.auth.token.creator == true;
allow create: if request.auth.token.creator == true;
}
The java code will now work.
What am I missing here? why is update blocking creates?
p.s.
Just to make sure, I'm removing all documents from my collection in firestore before running the code. Either way though, "add" method generates a random id, so it shouldn't matter.
Related
I have written some firebase rules, using firebase emulator. I tried to debug the request which I am sending to the rules, using debug() function. Yet it doesn't print anything. My rules:
function printResource() {
return debug(resource) || true;
}
match /users/{userId} {
allow read, delete: if something....;
allow create: if printResource() && userIsAuthenticated() && userOwnsResource(userId) && debug(isUserNameAvailable(debug(request))) && getAfter(
/databases/$(database)/documents/usernames/$(request.resource.data.username)
).data.owner == userId;
}
I tried as well the following:
match /users/{userId} {
allow read, delete: if something....;
allow create: if debug(userIsAuthenticated()) && debug(userOwnsResource(userId)) && debug(isUserNameAvailable(debug(request))) && getAfter(
/databases/$(database)/documents/usernames/$(request.resource.data.username)
).data.owner == userId;
}
Nothing is being printed. My debug() function is not working, but I don't know why. Is there another way I can print my resource object?
I don't get this running, no matter what I do. I have already removed all rules, nevertheless I get simulated read denied. I habe tried companies/4U4kZKXkr3rHA6B04S5K and /companies/4U4kZKXkr3rHA6B04S5K as location, copy pasted the document id and the collection multiple times, nothing... To me, it looks just like all the running examples, I found
What am I doing wrong?!
UPDATE: I used these rules before, which did not work:
match /databases/{database}/documents {
// the request object contains info about the authentication status of the requesting user
// if the .auth property is not set, the user is not signed in
function isSignedIn() {
return request.auth != null;
}
// return the current users entry in the employees collection
function getEmployeeData() {
return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data
}
// check if the current user has access to specific company
function accessCompany(companyId) {
return isSignedIn() && getEmployeeData()['companyId'] == companyId;
}
// check if the current user has a specific role
function hasRole(role) {
return isSignedIn() && getEmployeeData()[role] == true;
}
// check if the user has any of the given roles (list)
//function hasAnyRole(roles) {
// return isSignedIn() && getRoles().keys().hasAny(roles);
//}
}
match /users/{user} {
// anyone can see a specific users profile data (name, email etc), in a real scenario you might want to make this more granular
allow get: if true;
// noone can query for users
allow list, create: if false;
// users can modify their own data
allow update, delete: if request.auth.uid == user;
}
match /employees/{user} {
// only allow admins to set roles. Of course a user should be able to retrieve its own designated roles
allow get: if request.auth.uid == user || hasRole('admin');
allow list: if hasRole('admin');
allow update: if hasRole('admin');
allow create, delete: if false;
}
match /companies/{document=**} {
allow get, list, create, update, delete: if true;
}
}
By default, no read and write access is allowed to any document. If you want to allow access to a document, you must have at least one rule that matches the query that would allow that access. If you have commented out all your rules, then I would expect no reads or writes to be allowed.
Minimally, adding a rule like this will allow read access to all documents in the companies collection:
match /companies/{id} {
allow read: if true;
}
I suggest reviewing the documentation on security rules to better learn how they work.
Given the below rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Nobody except superuser and member can see member data
match /Member/{id} {
allow get: if ((request.auth != null) && (id == request.auth.uid));
allow list: if ((request.auth != null) && (id == request.auth.uid));
//allow read: if ((request.auth != null) && (id == request.auth.uid));
allow update: if ((request.auth != null) && (id == request.auth.uid));
allow delete: if ((request.auth != null) && (id == request.auth.uid));
allow create: if ((request.auth != null) && (id == request.auth.uid));
}
}
}
And my data: Member collection.
document ID: PIijFvYPXQPSlheQxotvFvXW4VI2 which has some fields
document ID: QM6tGSkA2SRSPjHGIurDiARd6zv1 which has some fields
My observations that make sense:
1) Using the Rules playground
Simulation type: get
Location: /Member/PIijFvYPXQPSlheQxotvFvXW4VI2
Authenticate: false
Result:
Simulated read denied, due to : "allow get: if ((request.auth != null) && (id == request.auth.uid));" This makes total sense.
2) Using the Rules playground
Simulation type: get
Location: /Member/PIijFvYPXQPSlheQxotvFvXW4VI2
Authenticate: true
Provider: Google
Firebase UID: QM6tGSkA2SRSPjHGIurDiARd6zv1
Result:
Simulated read denied, due to : "allow get: if ((request.auth != null) && (id == request.auth.uid));" This makes total sense.
3) Using the Rules playground
Simulation type: get
Location: /Member/PIijFvYPXQPSlheQxotvFvXW4VI2
Authenticate: true
Provider: Google
Firebase UID: PIijFvYPXQPSlheQxotvFvXW4VI2
Result:
Simulated read allowed. This makes total sense.
4) In flutter,
Codesnippet 1:
await AbstractRepositoryS
ingleton.singleton
.userRepository()
.signInWithGoogle()
.then((value) async {
await AbstractRepositorySingleton.singleton
.memberRepository().valuesList().then((event) {
print("Success");
});
Codesnippet 2 (implementation of AbstractRepositorySingleton.singleton.memberRepository().valuesList())
Future<List<MemberModel>> valuesList() async {
return await MemberCollection.getDocuments().then((value) {
var list = value.documents;
return list.map((doc) => _populateDoc(doc)).toList();
});
}
Codesnippet 3:
final CollectionReference MemberCollection = Firestore.instance.collection('Member');
Action:
I run code snippet 1, which requests me to login onto my phone. I login with firebase UID = PIijFvYPXQPSlheQxotvFvXW4VI2, then a query for ALL members is run.
Result:
This fails, which ones again all makes sense.
My observations that make NO sense:
Codesnippet 1:
await AbstractRepositorySingleton.singleton
.userRepository()
.signInWithGoogle()
.then((value) async {
await AbstractRepositorySingleton.singleton
.memberRepository().values().listen((event) {
print("Success");
});
Codesnippet 2 (implementation of AbstractRepositorySingleton.singleton.memberRepository().values())
Stream<List<MemberModel>> values() {
return MemberCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => _populateDoc(doc)).toList();
});
}
Action:
I run code snippet 1, which requests me to login onto my phone, I login with user with firebase ID = PIijFvYPXQPSlheQxotvFvXW4VI2,
then a LISTEN for ALL members is executed.
Result
This succeeds and the _populateDoc allows me to read all data fields from all members.
Basically it seems this mechanism allows me to read the entire collection, even for data where I should have no access to.
However, I'm sure I'm doing something wrong... but what.
Please advise. Many thanks.
One thing I noticed is an error occurring during the listen() method:
com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
Still for some reason I had 4 hits in the breakpoint in _populateDoc where I could happily read the member data from the doc.
After given this a bit extra thoughts, I've come to the conclusion this might have to do with local caching of the firestore data on my phone.
So I tried another phone, one where this particular app never ran before, I got the same error and I had 1 hit in the breakpoint in _populateDoc.
It's starting to make a bit sense to me. However, if someone could confirm this. Is this somewhere documented?
So, I typically don't have problems writing firestore rules but I'm coming up short on why this simulation fails.
If you don't want to follow the image link:
Rules:
service cloud.firestore {
match /databases/{database} {
match /server_credentials/{document = **} {
allow read, write: if false;
}
match /users/{user_uid} {
allow read, write;
match /signatures/{sig_id} {
allow read: if request.auth.uid != null && resource.data.access == "public";
allow read, write: if request.auth.uid == user_uid;
}
match /identity/{credential} {
allow read, write: if request.auth.uid == user_uid;
}
}
match /signatures/{sig_id} {
allow read, list;
allow write: if false;
}
}
}
Auth details:
Google as provider and foo as the uid.
I'm testing a simple read to users/foo
Any help would be quite nice.
This line is incorrect:
match /databases/{database} {
It should be this:
match /databases/{database}/documents {
This mistake is causing none of your rules to apply at all, and all access is being rejected by default.
I'm currently using Firestore for database and ran in to insufficient permission error. What I don't understand is why isPartner() will work when my path is
users/{uid}
but give me permission error when it's
object/{objectID}
Security Rules
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
allow read: if isSignedIn();
allow write: if isOwner(uid) || isPartner();
match /object/{objectID} {
allow read, write : if isPartner() || isOwner(uid);
}
function isOwner(userID) {
return request.auth.uid == userID
}
function isPartner() {
return resource.data.partnerUID == request.auth.uid
}
}
}