Firebase rule get target user uid - swift

I'm writing a Firebase rule to allow user a to access user b's 'Test' property:
"Test": {
".read":"root.child('Users').child(auth.uid).child('Test').child('subtest').val().contains('xxxxxxxxxxxxxxxxMdv3wQRjIs2')",
}
Just figured out that the rule checks on the requestor (user a) not the target user b (could be wrong :) ).
Is there any way to write a rule to represent the target user's uid like 'auth.uid' for the requestor?
Or anything could be done at the target user level?

Is this what you're looking to do?
"$target":{
".write": "root.child('path').val() == 'Value'"
}
or
"Users": { //Path to where you store your users (case sensitive)
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
EDIT: OP found the solution using ".read": "data.child('allowed').val() == auth.uid",

This should get you the user's UID:
let userID = FIRAuth.auth()!.currentUser!.uid

Related

realtime database rules wildcard

as soon as I use a wildcard I get this error:
Unhandled Exception: [firebase_database/permission-denied] Client doesn't have permission to access the desired data
works:
{
"rules": {
"user": {
".write": "auth != null",
".read": "auth != null"
}
}
doesn't work:
{
"rules": {
"user": {
"$userId": {
".write": "auth != null && $userId === auth.uid",
".read": "auth != null"
}
}
}
doesn't work:
{
"rules": {
"user": {
"$userId": {
".write": true,
".read": true
}
}
}
db structure:
code:
List<User>? userlist;
late Query query;
void initState() {
userAuth.FirebaseAuth.instance.authStateChanges().listen((userAuth.User? user) {
final FirebaseApp abcApp = Firebase.app();
final FirebaseDatabase database = FirebaseDatabase.instanceFor(app: abcApp);
userlist = [];
query = database
.ref().child("user").orderByChild("userId")
.equalTo(
ownUid==true
?user!.uid
:widget.peerId
);
_onOrderAddedSubscription1 = query.onChildAdded.listen(onEntryAdded1);
_onOrderChangedSubscription1 = query.onChildChanged.listen(onEntryChanged1);
});
super.initState();
}
My guess is that you're trying to read from /users. If you do that with the second set of rules, it gets rejected as those rules don't grant anyone permission to read all of /users - but only allows one to read /users/$uid.
It helps to recall that security rules on their own don't filter data, but instead merely check whether all data access is authorized.
So if you want to allow reading from /users, you need a rule on /users that allows that read. And if you want to allow reading specific data under /users, you either need to read from that specific path or combine a query and rules so that the rules can verify that the client is only reading data they're authorized for.
This problem comes up quite regularly, so I also recommend checking out more questions about 'rules are not filters'

How can I securely allow everyone to read and auth users to write to application with Firebase Realtime database?

I am building a social media application in which anyone can read what is going on but have to sign in to interact with each post. I would like to find a way to allow all my users to be able to do this without having an insecure database. I am almost done with development and I am currently trying to clean up a few things.
My current Rules are set to true for development purposes:
{
"rules": {
".read": true,
".write": true
}
}
I have tried a few rules
1.
{
"rules": {
"$uid": {
".read": true,
".write": "auth.uid == $uid"
}
}
}
Which allows everyone to read but doesn't allow writing due to permissions.
2)
{
"rules": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
}
}
Which doesn't allow anyone to read or write due to permissions.
3.
{
"rules": {
".read": "auth.uid != null",
".write": "auth.uid != null"
}
}
Which works but I get an email later telling me my database is insecure.
Here is an example that causes a permission denied error:
A) When the user clicks the send new announcement button
This func is called.
StorageService.sendAnnouncementDataToDatabase(photoUrl: "", announcementComment: announcementComment, ratio: CGFloat(0), onSuccess: {
ProgressHUD.showSuccess("SWEET")
})
B) Inside sendAnnouncementDataToDatabase func
let ref = Ref().databaseAnnouncements
let newAnnouncementID = ref.childByAutoId().key ?? ""
let newAnnouncementReference = ref.child(newAnnouncementID)
guard let currentUser = Api.User.CURRENT_USER else {
return
}
var dict = ["userId" : currentUserId, "photoUrl" : photoUrl]
newAnnouncementReference.setValue(dict, withCompletionBlock: {(error,ref) in
// permission denied occurs here
})
I have read through many documents, and stack question. As well as tried to set rules for each path. Same outcome.
Any ideas would be helpful.
Thank you.
If this is the actual goal
in which anyone can read what is going on but have to sign in to
interact with each post
then this rule will do it
{
"rules": {
".read": true,
".write": "auth != null"
}
}
As it allows anyone to read anything, but only authenticated users can write.
However, it's pretty insecure as Frank mentioned in his comments. We may be able to expand on this solution a bit but without understanding the entire use case it could go well beyond what can be posted here as a full answer.

How do you alter a a snapshot of the main child in order to not create security rule problems?

A = Database.database().reference().child("users");
A.observe(DataEventType.value, with: {snapshot in
The rules:
"rules": {
"users" :
".read": "auth.uid != null",
".write": "auth.uid != null" {
"$uid": {
"Garden" :{
".read": "auth.uid != null"
,".write": "$uid == auth.uid"
}
"Hose": .....
"House": ....
So as you'll see the users is not being assigned a rule because it would override rules for Garden, House, Hose etc. Therefore, I want to alter the code that looks for the snapshot in A to go more directly to the childs of users, vs stopping at users and consequently not passing the security test (since users has no rules).
What comes after A.observe:
for users in snapshot.children.allObjects as! [DataSnapshot] {
let usersObject = users.value as? [String: AnyObject]
let usersGarden = usersObject?["Garden"] as? String
let usersHose = usersObject?["Hose"] as? String
let usersHouse = usersObject?["House"] as? String
......
let USA = UserH(Garder: usersGarden, Hose: usersHose...)
self.users.append.
self.table.reloadData()
You'll see that the targets are the childs that come after uid, so a way to target them could allow me to avoid stopping at users in the snapshot A
As is, the code and the rules in the question match, the code reads the users node and the rules allow any authenticated user to read that node iterate over the child data.
I believe your actual question is
How do I get a more granular control of what can be written to a child
node in Firebase using Firebase Rules
suppose we have a structure that matches your rules
users
uid_0
garden: "rose"
hose: "green"
uid_1
garden: "tomato"
hose: "black"
and suppose we only want to allow a user to update their own garden; other users cannot modify that users garden. e.g. uid_0 can modify only the node /users/uid_0/garden. uid_1 can only modify /users/uid_1/garden etc.
Here's a rule that lets all authenticated users read the users node but only the currently authenticated user can write to their own garden node
{
"rules": {
".read": false,
".write": false,
"users" : {
".read": "auth != null",
"$uid": {
"garden": {
".write": "$uid === auth.uid" //only the authenticated user can write
}
}
}
}
}
Note that I set read and write to false at a high level and am granting access at a lower, more granular level.
So in this case, if there was another node at the same level as 'users' nobody could read or write to it.

Create hidden user node using firebase realtime

I am having some issues with Firebase security rules. I want any user to be able to create an account using my iOS app at the same time once he or she establishes the account I want to have two nodes one is private, and one is public. I want the public to be accessed by anyone, including the user its self, but the user that created the account only accesses the private node. I have tried a lot of things, but none of my work seems to work. I want to fetch all the public node values by just having a link without knowing the uid of each user
1- Anyone can create an account
2- Only the user can access his own private node
3- A link where I can fetch all of the user's public node only
Thank you!!!!
for example, I would like to fetch all the users https://id.firebaseio.com/Ireland.json
Here is some of my work
{
"rules": {
"Ireland": {
"users": {
"$uid": {
"private": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
},
"public": {
".read": true,
".write": "auth != null && auth.uid == $uid",
},
},
},
},
},
}
here is my swift code, but every time I try to create an account it says permission denied
guard let uid = result?.user.uid else { return }
let privateValues = ["email": email, "username": username, "password": password, "profileImageUrl": profileImageUrl, "country": "Ireland"]
let publicValues = ["username": username, "profileImageUrl": profileImageUrl]
let values = [uid: ["private": privateValues, "public": publicValues]]
Database.database().reference().child("Ireland").child("users").updateChildValues(values, withCompletionBlock: { (error, reference) in
if let error = error {
print("Failed to save user info to database: ", error)
self.signUpButton.wiggle()
return
}
Firebase security rules cannot be used to filter data. All they do is check if a certain read operation is allow, without checking each individual node.
So if you attach a listener to /Ireland, the server checks if the current user has read permission to /Ireland. Since nobody has permission on that level, the read operation is rejected.
This is also known as 'rules are not filters' in both the documentation and previous questions.
Last year Firebase added support for validating queries in security rules, so that for example you can allow queries that also filter by a ownerUID type property. See the documentation on query based rules for more on that.
But that won't work for your use-case either, since read operations always return full nodes. Which brings us back to the fact that security rules can't be used to filter data.
You will have to separate the public and private data into two separate top-level nodes: private and public. This is one of the many reasons the Firebase documentation recommends keeping your data structure flat.
Also see:
How to create public/private user profile with Firebase security rules?
Firebase: How to structure public/private user data

Limiting non premium users to write to firebase database only once a day

I want to limit my non premium users to only post one post per day. Premium users have no limit. I am unclear how to set up my database rules. At the moment I have only validated users can post to the database. note I want to write to the AddPost node
My database Json looks like this:
User :
uid:
verified : true
premium : false
And my rules:
{
"rules": {
"AddPost": {
".read": "auth != null",
".write":
"root.child('Users').child(auth.uid).child('verified').val() == true"
}
,"Users": {
".read": "auth != null",
".write": "auth != null"
}
,"FavoritedBusinesses": {
".read": "auth != null",
".write": "auth != null"
}
,"Geolocs": {
".read": "auth != null",
".write": "auth != null"
}
,"ReviewPost": {
".read": "auth != null",
".write": "auth != null"
}
,"Views": {
".read": "auth != null",
".write": "auth != null"
}
}
}
MY SOLUTION
So using Franks guidance I was able to string some rules together. As a base rule every user must be verified so I put this in the "validate" condition as its a common denominator.. then in the write first we check to see if the user is a premium if it returns false then we check the last time stamp from the one we stored in the last post. However if the first condition fails then that means the user is infact premium and should be allowed to post..
new database rules
".write": "root.child('Users').child(auth.uid).child('premium').val()
=== false && (newData.parent().child('Users').child(auth.uid).child('lastPostStamp').val() === now) && (newData.parent().child('Users').child(auth.uid).child('lastPostStamp').val() > root.child('Users').child(auth.uid).child('lastPostStamp').val() + 24*60*60*1000) || root.child('Users').child(auth.uid).child('premium').val() === true ",
".validate": "root.child('Users').child(auth.uid).child('verified').val() === true"
As Doug commented, this is possible by:
Requiring that each time the user writes a post, they also write a timestamp to a known location (say /Users/$uid/last_post_timestamp).
Validating that they can only write ServerValue.TIMESTAMP to that location.
Validating that they can only write a new post when the current value in /Users/$uid/last_post_timestamp is at least one day ago.
So you'd need something along these lines in the rules for the new post:
(newData.parent().child('Users').child(auth.uid).child('last_post_timestamp').val()
=== now) &&
(newData.parent().child('Users').child(auth.uid).child('last_post_timestamp').val()
> root.child('Users').child(auth.uid).child('last_post_timestamp').val() + 24*60*60*1000)