Reputation: 106
Specific TLDR: The rule ".read": "auth != null && data.child('userEmail').val() === root.child('users').child(auth.uid).child('email').val()"
should work as expected in the app.
I've followed the documentation and in the rules playground the test works so I think it has to do with the way I'm authenticating maybe? I'll provide the info below and hopefully someone can answer this soon.
Realtime Database structure:
"db-name": {
"units": {
0: {
"serial": "002",
"userEmail": "[email protected]"
},
1: {
"serial": "001",
"userEmail": "[email protected]"
}
},
"users": {
"R6nlZ...": {
"email": "[email protected]"
},
"qwerty...": {
"email": "[email protected]"
}
}
}
Rules object:
{
"rules": {
// ".read": "now < 1604037600000", // 2020-10-30
// ".write": "now < 1604037600000", // 2020-10-30
"units": {
".indexOn": "userEmail",
"$key": {
".read": "auth != null && data.child('userEmail').val() === root.child('users').child(auth.uid).child('email').val()",
".write" : "auth != null && data.child('userEmail').val() === root.child('users').child(auth.uid).child('email').val()"
}
},
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
Rules test: Simulation type: read Location: https:db-name.firebaseio.com/units/1 Auntenticated: yes Provider: Anonymous UID: R6nlZ... Result: Simulation read allowed
If I try to get /units/0
I get denied which is what I expect because that's a unit
that the current auth'd user doesn't have permission to see.
Now if I do this in plain javascript I don't get the same result as I do in the Rules Playground in the Firebase Console.
<!-- Add the entire Firebase JavaScript SDK -->
<script src="https://www.gstatic.com/firebasejs/7.24.0/firebase.js"></script>
<script>
// import * as firebase from "firebase";
var firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "..."
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
var email = '[email protected]';
var password = '123456';
// Sign in existing user
firebase.auth().signInWithEmailAndPassword(email, password).then(function() {
console.log('Signed in as: ' + firebase.auth().currentUser.email);
var dbUnits = firebase.database().ref().child('units').orderByChild('userEmail').equalTo(firebase.auth().currentUser.email);
var getUnitsDetails = function() {
return dbUnits.once('value').then(function(snapshot) {
snapshot.val() ?
Object.entries(snapshot.val()).map((unit) => {
return (
console.log('Serial number of unit: ' + unit[1].serial + ' & Owner of unit: ' + unit[1].userEmail)
)
})
:
console.log('no units found for that user bro')
})
}
console.log(getUnitsDetails());
}).catch(function(error) {
console.log(error);
});
When I have a Firebase permissions set as they are above in the rules I pasted, the user [email protected]
can't see any units. If I let the read permissions be fully open (not what I want) then that user can see their unit(s).
To me this doesn't make sense because I thought auth.uid
is what Firebase can see when the user is logged in no matter what login type they use.
Upvotes: 0
Views: 1032
Reputation: 598765
This query is not allowed by your rules:
firebase.database().ref().child('units').orderByChild('userEmail').equalTo(firebase.auth().currentUser.email);
For this query to work, the user needs to have read permission on /units
. Since you don't declare any .read
rule on /units
the query is rejected.
You seem to think that the rules will filter the data to only return the data that the user should have access to, but that is not how they work. As the Firebase documentation says: rules are not filters.
But security rules can be used on combination with queries, to securely allow querying of the data.
In your scenario that'd look something like:
{
"rules": {
"units": {
".read": "auth.uid != null &&
query.orderByChild == 'userEmail' &&
query.equalTo == auth.token.email"
...
Now your query will be allowed, while other/broader reads of /users
will be denied.
Upvotes: 1
Reputation: 305
"$key": {
".read": "auth !== null && root.child('users').hasChild(auth.uid)",
".write" : "auth !== null && root.child('users').hasChild(auth.uid)"
}
Written like this any user that exists in user collection will have access to "units"
Alternatively you can make collection with that user's uid and match the key with his uid for access
Upvotes: 0