1
votes

I am getting very confused with the Firebase rules structure. Basically I have a root folder called 'Transactions', containing many childByAutoIDs that again contains data, exactly like this:

Transactions {
    autoID1 {
        transactionID: whatever,
        timeCreated: whatever,
        "amount": whatever,
        "sender": whatever,
        "receiver:whatever"
    }
}

I am now trying my best to implement the best possible security rules. I therefore tried to do something like this:

"Transactions": {
    ".write": "auth !== null",
    "$transactionAutoID": {
        "timeCreated": {
            ".write": "auth !== null && data.child('sender').val() === auth.uid || data.child('receiver').val() === auth.uid",
            ".read": "auth !== null && data.child('sender').val() === auth.uid || data.child('receiver').val() === auth.uid"
        }
    }
}

So basically, from what I have learned, and also what I am struggling with - is the fact that the user is able to write anything, even in the 'wildcard IDs ($transactionAutoID)'. I know this is because I have set the '.write' in Transactions to 'auth !== null' - but if I don't have this set, users may not read or write any transaction data. (I have the rules by default set to false).

How would I proceed if I only wanted users to be able to create new childs in Transactions, but not write in any transaction keys, if they are not either the sender or the receiver?

1

1 Answers

1
votes

Once a permission is granted on a node, it cannot be taken away on a lower-level node.

So if you look at this:

"Transactions": {
    ".write": "auth !== null",
    "$transactionAutoID": {
        "timeCreated": {
            ".write": "auth !== null && data.child('sender').val() === auth.uid || data.child('receiver').val() === auth.uid",
            ".read": "auth !== null && data.child('sender').val() === auth.uid || data.child('receiver').val() === auth.uid"
        }
    }
}

The write rule on Transactions/$transactionId/timeCreated is meaningless, since it tries to tighten the permission from Transactions.

So right now it functions like this:

"Transactions": {
    ".write": "auth !== null",
    "$transactionAutoID": {
        "timeCreated": {
            ".read": "auth !== null && data.child('sender').val() === auth.uid || data.child('receiver').val() === auth.uid"
        }
    }
}

If you want to put a limit to specifically what a user can do on Transactions/$transactionId/timeCreated, you will either have to do so in a validation rule or (more likely) by putting all the rules on the transaction itself:

"Transactions": {
    "$transactionAutoID": {
        ".write": "auth !== null && (
            !data.exists() || (
                newData.child('sender').val() === auth.uid ||
                newData.child('receiver').val() === auth.uid)"
            )
        )"
    }
}

In words: you can write a transaction when you're authenticated and either the transaction doesn't exist yet or you're the sender or receiver of the transaction.