For some reason the following security rule is resolving to false when I try to write an object without the property that should be verified in .hasChild(newData.child('ownerId').val()). The property isn't mandatory, so I'm up to accept a write without it.
"pizza": {
"$pizzaId": {
".write": "root.child('users').hasChild(newData.child('ownerId').val()) || true"
}
}
Thus, I'm getting a PERMISSION_DENIED when running something like the code bellow:
firebase.database().ref(`pizza/peperoneID`).set({
extraPepe: true
});
I know that I can fix it by just going with (newData.child('ownerId').exists() && .hasChild(...)) || true but I'm really trying to understand why the first option isn't enough.
If there is no ownerId, you'll be passing null when you call hasChild.
That'll effect an error and that error will see your rule fail - so the trailing || true is ineffectual.
Related
I got an "object" on my realtime database.
-MHHP5ZSmLKG_xeA9SLJ
0NThxhcHIPgOJGZC1MyE3Fg0NUc2
XCEI5dQxP8YxaChrF5O061eyFv32
creatorID: "7Pao7pDRaFUVWM9c234Qq44UwoE3"
message: "I wish I knew, why the world is is getting craz..."
messageID: "-MHHP5ZSmLKG_xeA9SLJ"
timestamp: 1600184270739
timestampReverse: 998399815846742
Two questions.
Is there a way for me to make it, such that creatorID: can't be written to after the original initialization.
"creatorID" : {
.write : "false"
}
Does this work, or would it also block initial write? Essentially I'm looking to make it unable to be edited.
And is there a way to writea rule such that only if auth.uid and creatorID.value is equal, can you edit the message:
"message" : {
.write : "auth.uid == creatorID.value"
}
Best regards,
Sam
If you want the creatorID field to only be writeable upon creation, you can do:
"creatorID" : {
.write : "!data.exists()"
}
If you want the message field to only be writeable by its original author, that'd be:
"message" : {
.write : "auth.uid == data.child('creatorID').val()"
}
match /UserProfile {
match /{uId}{
allow get: if isUserLoggedIn() && !isUserBlocked(uId);
}
when i try to get data from UserProfile/{uId} using the above security rules it throws the following error in the firestore and in code it says insufficient permissions:
Error running simulation — Error: simulator.rules line [199], column [28]. Function not found error: Name: [get].
now the definition of above two function are here
function isUserLoggedIn(){
return request.auth != null;
}
function isUserBlocked(uId){
return (('blocked' in get(/databases/$(database)/documents/UserSettings/$(uId)).data) && (request.auth.uid in get(/databases/$(database)/documents/UserSettings/$(uId)).data.blocked));
}
the first function runs very well
but the second one doesn't
it throws that error
and as of my knowledge the function is alright
please help i have wasted a whole lot of time on this piddly problem
what i have tried
i read in one of the threads that it is a temporary problem
but it is not like that. its been more than 48 hours now
somewhere it was also mentioned that this is a bug only in simulator but the code will run smoothly and even this is not the case. in code the error is insufficient permissions as expected by me
i have read the docs properly so my code is alright have tested the get method in other rules and there it is working fine
thats it please help
Update: The errors are a bug in the rules simulator, see Doug's comment below.
I tried out your rules and they worked as expected in the simulator.
Rules:
match /UserProfile {
function isUserLoggedIn(){
return request.auth != null;
}
function isUserBlocked(uId){
return (('blocked' in get(/databases/$(database)/documents/UserSettings/$(uId)).data) && (request.auth.uid in get(/databases/$(database)/documents/UserSettings/$(uId)).data.blocked));
}
match /{uId}{
allow get: if isUserLoggedIn() && !isUserBlocked(uId);
}
}
Test query in simulator:
get /UserProfile/foo
Authenticated: Yes
Firebase UID: bar
The request succeeds or fails based on the UserSettings/foo document in the database:
Denies request:
/UserSettings/foo
{
content: "my content"
blocked: { bar: true }
}
Allows request:
/UserSettings/foo
{
content: "my content"
blocked: { otheruser: true }
}
I think that errors can pop up when the data doesn't exist or isn't in the expected format.
For example, if I delete the /UserSettings/foo document I also receive:
Error: simulator.rules line [58], column [28]. Function not found error: Name: [get].
I also get this error if the blocked field is anything other than a map (because in is a function for maps):
Error: simulator.rules line [58], column [95]. Function not found error: Name: [in].
You can probably clean up these errors by using exists and checking the type of blocked but either way, your rules should still deny the request as expected.
I posted a question about this yesterday but I'm creating a new one with more details.
Firestore .setData is blocked by update rule not create
I've run the simulator and the rules work there. Also when I create the document and change setData in the swift code to update the code works. It appears to only fail when creating the document. But the catch is that when I remove the update rule or simply change it to allow update: if false; the setData (or seen as create by the rules) executes properly. I have no clue whats going on nor do I know of any tools for getting a better insight.
match /users_real/{userID} {
allow create: if true;
allow read: if isOwner(userID);
allow update: if (request.writeFields.size() == 1);
}
set data:
self.docRef.collection("users_real").document("adfadsf").setData(post) { (error) in
if let error = error {
print("He dead!: \(error.localizedDescription)")
}
else {
print("it worked, for now")
}
}
Firebase Support confirms that there is a bug related to the evaluation of request.writeFields.size(). No estimate was given of when it will be fixed.
The existence of the bug can be demonstrated with the following rules:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
// This should always evaluate to true, but does not.
allow create: if (request.writeFields.size() == 1) || (request.writeFields.size() != 1);
allow update: if true;
}
}
}
Although the create rule should always evaluate to true, an attempt to create a city fails with Permission Denied. It seems that the problem with request.writeFields affects not only the rule in which it appears, but also other rules for the path. For the rules shown above, an attempt to update an existing city also fails with Permission Denied.
I'm using priority on the object to check permissions of the user. However I'm experiencing strange behavior - when I try to compare priority with greater then or equal operator (>=) in DB security rules (realtime DB), I get permission denied error. However, when I use equal operator (===), then it works fine. I need to use >= operator, as I'm checking minimal permissions value for given action.
Here is concrete example:
{
"rules": {
"tiles": {
"$boardId": {
".read": "root.child('boards/'+$boardId+'/owner').val() === auth.uid || root.child('boards/'+$boardId+'/visibility').val() === 'public' || root.child('boardsPermissions/' + $boardId + '/' + auth.uid).getPriority() >= 10",
".write": "root.child('boards/'+$boardId+'/owner').val() === auth.uid || root.child('boardsPermissions/' + $boardId + '/' + auth.uid).getPriority() >= 20"
}
}
}
}
If try to write to location tiles/board-1/tile-1 with user user-1 with this DB record in permissions node:
'permissions/board-1/user-1': {
.priority: 20
}
I got an error, however if I change the write rule to form below, I can write to given node.
".write": "root.child('permissions/' + $boardId + '/' + auth.uid).getPriority() === 20"
I know that first condition is false (user is not owner of the tile). I believe this may be a bug, but I want to confirm it first. Can please someone have a look?
What is also strange is, that it start working once and I made successful write to DB, but then it stop working again. Is there some delay between publishing security rules and them taking effect in DB?
Thanks for help.
I'm having a hard time figuring out how the Firebase rules should be in order for me to restrict the reading/writing as I'd like.
I'm working on a queue system in Firebase, where the structure is as follows
"eventQueues": {
"event1": {
"owner": "simplelogin:1",
"queue": {
"simplelogin:2": {"someinfo": "..."},
"simplelogin:3": {"someinfo": "..."},
"simplelogin:4": {"someinfo": "..."}
}
},
"event2": {...},
...
}
I would like to achieve the following:
Anybody can read the data
".read": true
Any user can create (and edit any part of) an event and but has to be the owner of it.
I believe this can be achieved by:
".write" : "auth != null &&
(data.child('owner').val() == auth.uid ||
newData.child('owner').val() == auth.uid)"
Any user can add their own entry to the queue of an event but may not change anything else in the queue, nor remove themselves from the queue.
This is where I am having trouble, and the best I have been able to come up with is the following (which will be combined with the write-rule above)
".write": "!data.child(auth.uid).exists()"
But as far as I can tell, this still allows the users to write to anything but the queue-entry corresponding to their own uid.
So can I in any way make a rule that allows non-event-owners only to add themselves to the queue, and nothing else?