When I upgraded to Firebase 8.12.1, the local database emulator gets a Java/JavaScript error parsing the database rules. I trimmed the original rules down to the smallest thing that breaks. Here's what database-debug.log shows, including the JSON input. Bug or did Firebase change some syntax? JSONLint says the JSON itself is OK.
ERROR com.firebase.core.namespace.NamespaceActor - Unexpected error caught in NamespaceActor(...) for AdminEnvelope(SecurityClaims(Some(GCloudToken(owner,GCloudOwner,1602893571,None)),None),UpdateRules({
"rules": {
".read": true,
"tickets": {
"$offering_id": {
"tickets": {
"blocks": {
"$block_id": {
".validate": "newData.hasChildren(['timestamp', 'text']"
}
}
}
}
}
}
}
,false))
java.lang.ExceptionInInitializerError: null
at org.mozilla.javascript.Context.getCurrentContext(Context.java:346)
Related
not able to insert record in firebase table using Angular 6, I am using real-time database
Component code below:-
*CreateRecord() {
let jsonob: jsonob = {
'MSG-21A7605D-A48E-4291-ABDA-4DFE046FE597': {
msg_text: 'Test Person1'
}
};
this.crudService.create_NewStudent(jsonob).then(resp => {
this.msg_text = "";
console.log(resp);
}).catch(error => {
console.log(error);
});
}
create_NewStudent(record) {
return this.firestore.collection('messages').add(record);
}
Firebase Rules is below:-
{
"rules": {
".read": true,
".write": true
}
}
messages table is below:-
https://i.stack.imgur.com/InWGE.png
Error Message:
FirebaseError: Missing or insufficient permissions.
at new FirestoreError (http://localhost:4200/vendor.js:93048:28)
Your question says "I am using real-time database", but the code is using Firestore. On top of that, you're showing rules for Realtime Database, which in no way affect access to Firestore. The error message "Missing or insufficient permissions" is suggesting that your Firestore security rules (not your Realtime Database rules) are disallowing access.
Basically, you're going to have to go back and make sure you're using the correct database system across the board.
These are my firestore security rules:
service cloud.firestore {
match /databases/{database}/documents {
match /collectionA/{someID} {
function checkA() {
return get(/databases/$(database)/documents/collectionA/$(someID)/users/$(request.auth.uid)).data.deleteFlag != true
}
function checkB() {
return get(/databases/$(database)/documents/collectionB/$(request.auth.uid)/companies/$(someID)).data.deleteFlag != true
}
allow read, write: if checkA() || checkB()
}
}
}
And this is my database objects:
project {
collectionA {
companyA {
users {
r9Myn4TfzAVpSZGzyaet {
deleteFlag: false
}
}
}
}
collectionB {
aAzUlfztdYdEIXT3Tva73kCiuy93 {
companies {
companyA{
deleteFlag:false
}
}
}
}
}
And I tried Simulator :
simulation type : get
location : collectionA/companyA
provider : password
Firebase UID : aAzUlfztdYdEIXT3Tva73kCiuy93
I expected this security rules return "true".
Because I thought checkA returned "false" and checkB returned "true".
But they returned "false".
Please let me know if you have any ideas or suggestions.
I heard that the simulator sometimes does not work properly.
I threw the query from the client and verified it and it worked correctly.
So I thought it was a bug in the simulator.
However, I received the following response from Developer Platform Support.
FYI
The get function will get an error if trying to get a value for an
object that does not exist. When an error occurs in the condition
judgment, the security rule is not applied in the security rule.
Because of the specifications of security rules, it is impossible to
control on nonexistent objects
I'm developing an app using Firebase's Realtime Database and need to allow multiple users to access the same data, but I'm having trouble figuring out a security rule that makes this work.
The database looks like this:
teams: {
teamID3ic3kic9w3jkck : {
userIDs: ["11111", "22222", "33333", "44444"]
teamData { ....}
}
}
where I want to allow users with an ID matching any of the IDs in the "userIDs" array to access "teamData". Would really appreciate help figuring this out.
Every time you're looking to do array.contains(), you're likely using the wrong data structure. For example, this seems more like a mathematical set to me: an unordered collection of unique items. In Firebase you'd model that as:
teams: {
teamID3ic3kic9w3jkck : {
userIDs: {
"11111": true,
"22222": true,
"33333": true,
"44444: true"
]
teamData { ....}
}
}
Now you can secure this with:
{
"rules": {
"teams": {
"$teamid": {
".read": {
".read": "data.child('userIDs').child(auth.uid).exists()"
}
}
}
}
}
I am using Firebase rules to set permissions and I am having trouble setting up rule to allow write permissions to include the user and any admin.
Below is the database I am trying to set permissions for:
'rules': {
'rankings': {
'user': {
'userShortenedAuthId': { //user's rankings },
'anotherShortenedAuthId': { //another user's rankings }
}
},
'admin': {
'adminAuthId': true
},
'users': {
'userAuthId': {
'rank': 'userShortenedAuthId'
},
'anotherAuthId': {
'rank': 'anotherShortenedAuthId'
}
}
}
These are the rules in place for this data:
"rankings":{
"user":{
"$rankId": {
".read": true,
".write": "auth != null && ((root.child('users/' + auth.uid + '/rank').val() == $rankId) || root.child('admin/' + auth.uid).exists())"
}
}
}
I am trying to write to a 'rankings/user/userShortenedId' while logged in under the admin, but I get a permission denied error from Firebase. (Logged in as the user works just fine). The error is somewhere in the 'rankings/user/$rankId/.write' rule. What is confusing for me, is I have been using the 'root.child('admin/' + auth.uid).exists())' rule elsewhere for admin privileges and that seems to work fine too.
Here is the code that produces the permission error.
firebase.database().ref('rankings/' + userShortenedAuthId).update({newPlayerKey:totalPlayers}))
.then(function(){
console.log('successfully updated user ranking');
})
.catch(function(err){
console.log('error updated user ranking', err);
});
Found the answer to my own question. The firebase ref URL that was referenced when trying to update
firebase.database().ref('rankings/' + userShortenedAuthId).update({newPlayerKey:totalPlayers}))
should actually be
firebase.database().ref('rankings/user/' + userShortenedAuthId).update({newPlayerKey:totalPlayers}))
So the rules were actually working correctly. I was simply trying to write to the wrong place.
I wrote following simple presence code in JavaScript (based upon https://firebase.google.com/docs/database/web/offline-capabilities#section-sample):
var app = firebase.initializeApp(config);
var mainRef = app.database().ref();
var session = null;
var connected = false;
function do_sessionSubscribe(subscription) {
if (!subscription.entry) {
subscription.entry = subscription.parent.push(true);
subscription.entry.onDisconnect().remove();
}
}
function do_sessionUnsubscribe(subscription) {
if (subscription.entry) {
subscription.entry.remove();
subscription.entry = null;
}
}
mainRef.child(".info/connected").on("value", function(snap) {
connected = snap.val() === true;
if (session) {
if (connected) {
do_sessionSubscribe(session.subscription);
} else {
// workaround
//do_sessionUnsubscribe(session.subscription);
}
}
});
function closeSession() {
if (session) {
do_sessionUnsubscribe(session.subscription);
session = null;
}
}
function openSession(uid) {
session = { uid: uid, subscription: { parent: mainRef.child("session/user/" + uid), entry: null } };
if (connected) {
do_sessionSubscribe(session.subscription);
}
}
app.auth().onAuthStateChanged(function(user) {
closeSession();
if (user && user.uid) {
openSession(user.uid);
}
});
Security rules:
"session": {
"user": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid",
"$session": {
".validate": "newData.val() === true"
}
}
},
}
The idea is that each active connection of a user will create /session/user/$uid/$session upon connecting/signing in and delete it when disconnecting/signing out.
Therefore in order to obtain a list of online users it should be sufficient to get /session/user with shallow=true.
The problem is that sometimes a session isn't cleaned up and stays under /session/user/$uid forever. This is then interpreted like if a user was online all the time.
I discovered that in order to easily reproduce the issue it is sufficient to block access to securetoken.googleapis.com (I use Google authentication), wait an hour and close the browser.
I tried to workaround the problem by calling remove() on disconnection. This cleans up the stale session as soon as the client gets reconnected (this is too late, but better late than never...). However, when user closes it's browser after loosing internet connection and then the auth token expires before sockets time out, the stale session persists forever.
What value of auth.uid is used during checking security rules when auth token used for registering onDisconnect() action is already expired?
How to make this presence system fully reliable without compromising security?