Thanks for very detailed answer!
To refer other comments too, my intention is exactly as you have mentioned; "give users the minimum requirements for both reading and writing, nothing more".
See my rules for users table (where user data that is needed for other purposes + User-Messages table).
{
"rules": {
".read": false, // disables general read access
".write": false, // disables general write access
"Users": {
".read": "auth != null", // Users can read all profiles
"$userUid": {
".write": "auth != null && auth.uid === $userUid", // Users can write only their own data
".validate": "newData.child('userUid').val() === auth.uid &&
newData.child('userID').isNumber() &&
newData.child('userName').isString() && newData.child('userName').val().length > 0 &&
newData.child('userFCMToken').isString() && newData.child('userFCMToken').val().length > 0 &&
newData.child('userPhoneLanguage').isString() &&
newData.child('userProfileImage').isString() &&
newData.child('userProfileImage').val().matches(/^https:\\xxxxx\\//) &&
(!newData.child('userBusiness1Id').exists() || newData.child('userBusiness1Id').isNumber()) &&
(!newData.child('userBusiness1Name').exists() ||
(newData.child('userBusiness1Name').isString() && newData.child('userBusiness1Name').val().length > 0)) &&
(!newData.child('userBusiness1ProfileImage').exists() ||
(newData.child('userBusiness1ProfileImage').isString() &&
newData.child('userBusiness1ProfileImage').val().matches(/^https:\\/\\xxxxx\\//))) &&
(!newData.child('userBusiness2Id').exists() || newData.child('userBusiness2Id').isNumber()) &&
(!newData.child('userBusiness2Name').exists() ||
(newData.child('userBusiness2Name').isString() && newData.child('userBusiness2Name').val().length > 0)) &&
(!newData.child('userBusiness2ProfileImage').exists() ||
(newData.child('userBusiness2ProfileImage').isString() &&
newData.child('userBusiness2ProfileImage').val().matches(/^https:\\/\\/xxxxxx\\//)))"
}
},
"User-Messages": {
".write": "auth != null",
".read": "auth != null",
},
I have tried your approach but couldn't figure it out how to define fromId, toId or pushId..
func sendMessage(text: String, fromId: String, toId: String) {
let trimmedText = text.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedText.isEmpty { return }
let timeStamp = Int64(Date().timeIntervalSince1970)
let reference = Database.database().reference().child("User-Messages").child(fromId).child(toId).childByAutoId()
let toReference = Database.database().reference().child("User-Messages").child(toId).child(fromId).childByAutoId()
let chatModel = ChatModel(chatId: reference.key, text: trimmedText, fromId: fromId, toId: toId, timestamp: timeStamp, messageIsRead: true)
let chatModelForRecipient = ChatModel(chatId: toReference.key, text: trimmedText, fromId: fromId, toId: toId, timestamp: timeStamp, messageIsRead: false)
reference.setValue(chatModel.toDictionary())
toReference.setValue(chatModelForRecipient.toDictionary())
}
This is the full code that how i post data into User-Messages.
I combine fromId or toId inside the view before calling sendMessage, as userUid_businessId.
I simply couldn't figure this out how should i define inside the rules where users can write/read since it is dynamic.
so sender should be able to write for both paths : user itself + message receiver.
Thanks!