Skip to content

Instantly share code, notes, and snippets.

@prilaga
Last active November 3, 2022 16:10
Show Gist options
  • Save prilaga/28ddd15f00e06feab5b850c53da4d4ed to your computer and use it in GitHub Desktop.
Save prilaga/28ddd15f00e06feab5b850c53da4d4ed to your computer and use it in GitHub Desktop.
{
"rules": {
// Admin access.
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
"admins": {
".read": "false", // Only a cloud function or admins can update this.
".write": "false", // Only a cloud function or admins can update this.
"$index": {
"email": {
".validate": "newData.isString()"
}
}
},
// User's personnal feeds.
"feed": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid",
"$postId": {
".validate": "newData.val() === true && newData.parent().parent().parent().child('posts').child($postId).exists()"
}
}
},
// List of all Posts.
"posts": {
".read": true,
".indexOn": ["author/uid", "timestamp"], // Allow searching for posts by authors. Useful for deletion.
"$postId": {
".write": "!data.exists() || data.exists() && auth.uid === data.child('author').child('uid').val()", // Allow new writes and allow updates and deletes to own posts.
".validate": "newData.child('author').child('uid').exists() && newData.child('text').exists()",
"text": {
".validate": "newData.isString() && newData.val().length < 2000"
},
"timestamp": {
".validate": "newData.val() === now"
},
"client": {
".validate": "newData.val() === 'web' || newData.val() === 'ios' || newData.val() === 'android'"
},
"sanitized": {
".validate": "false" // Only a cloud function (admin mode) can update this.
},
"moderated": {
".validate": "false" // Only a cloud function (admin mode) can update this.
},
"author": {
"uid": {
".validate": "auth.uid === newData.val()"
},
"full_name": {
".validate": "auth.token.name === newData.val() || (auth.token.name === null && newData.val() === 'Anonymous')"
},
"profile_picture": {
".validate": "true"
}
}
}
},
// Post's comments.
"comments": {
".read": true,
"$postId": {
".indexOn": "author/uid", // Allow searching for comments by authors. Useful for deletion.
".write": "!newData.exists() && auth.uid === root.child('posts').child($postId).child('author').child('uid').val() && !newData.parent().parent().child('posts').child($postId).exists()", // Allow deletes from the post owner
".validate": "root.child('posts').child($postId).exists()", // Check that the post exists
"$commentId": {
".write": "(!data.exists() || data.exists() && auth.uid === data.child('author').child('uid').val()) && (root.child('blocked').child(auth.uid).child(root.child('posts').child($postId).child('author').child('uid').val()).val() !== true)", // Can write new comments and edit/delete particular comment if you are the author.
"text": {
".validate": "newData.isString() && newData.val().length < 2000"
},
"sanitized": {
".validate": "false" // Only a cloud function (admin mode) can update this.
},
"moderated": {
".validate": "false" // Only a cloud function (admin mode) can update this.
},
"timestamp": {
".validate": "newData.val() === now"
},
"author": {
"uid": {
".validate": "auth.uid === newData.val()"
},
"full_name": {
".validate": "auth.token.name === newData.val() || (auth.token.name === null && newData.val() === 'Anonymous')"
},
"profile_picture": {
".validate": "true"
},
"$other": {
".validate": false
}
},
"$other": {
".validate": false
}
}
}
},
// Hashtag index.
"hashtags": {
".read": true,
".write": false
},
// List of post's likes.
"likes": {
".read": true,
"$postId": {
".write": "!newData.exists() && auth.uid === root.child('posts').child($postId).child('author').child('uid').val() && !newData.parent().parent().child('posts').child($postId).exists()", // Allow deletes from the post owner
".validate": "root.child('posts').child($postId).exists()", // Check that the post exists
"$uid": {
".write": "auth.uid === $uid",
".validate": "newData.val() === now"
}
}
},
// List of followers.
"followers": {
".read": true,
"$followedUid": {
"$followerUid": {
".write": "auth.uid === $followerUid", // Can only add yourself as a follower
".validate": "newData.val() === true && newData.parent().parent().parent().child('people').child($followerUid).child('following').child($followedUid).exists()" // Makes sure /people/.../following is in sync
}
}
},
// Public profile information.
"people": {
".indexOn": ["_search_index/full_name", "_search_index/reversed_full_name"],
".read": true,
"$uid": {
".write": "auth.uid === $uid",
"full_name": {
".validate": "auth.token.name === newData.val() || (auth.token.name === null && newData.val() === 'Anonymous')"
},
"profile_picture": {
".validate": "true"
},
"posts": {
"$postId": {
".validate": "newData.val() === true && newData.parent().parent().parent().parent().child('posts').child($postId).exists()"
}
},
"_search_index": {
"full_name": {
".validate": "newData.isString()"
},
"reversed_full_name": {
".validate": "newData.isString()"
}
},
"following": {
"$followedUid": {
".validate": "newData.parent().parent().parent().parent().child('followers').child($followedUid).child($uid).val() === true" // Makes sure /followers is in sync
}
}
}
},
// List of blocked users.
"blocking": {
"$blockerUid": {
".write": "auth.uid === $blockerUid", // Can only add yourself as a blocker
".read": "auth.uid === $blockerUid",
"$blockedUid": {
".read": "auth.uid === $blockedUid",
".validate": "newData.val() === true && newData.parent().parent().parent().child('blocked').child($blockedUid).child($blockerUid).exists()" // Makes sure /blocked is in sync
}
}
},
// List of blocking users.
"blocked": {
"$blockedUid": {
".read": "auth.uid === $blockedUid",
"$blockerUid": {
".read": "auth.uid === $blockerUid",
".write": "auth.uid === $blockerUid", // Can only add yourself as a blocker
".validate": "newData.val() === true && newData.parent().parent().parent().child('blocking').child($blockerUid).child($blockedUid).exists()" // Makes sure /blocker is in sync
}
}
},
// List of flagged posts.
"postFlags": {
"$postId": {
".validate": "root.child('posts').child($postId).exists()", // Check that the post exists
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid",
".validate": "newData.val() === true"
}
}
},
// List of flagged comments.
"commentFlags": {
"$postId": {
".validate": "root.child('posts').child($postId).exists()", // Check that the post exists
"$commentId": {
".validate": "root.child('comments').child($postId).child($commentId).exists()", // Check that the comment exists
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid",
".validate": "newData.val() === true"
}
}
}
},
// Privacy settings
"privacy": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid",
"data_processing": {
".validate": "newData.isBoolean()"
},
"content": {
".validate": "newData.isBoolean()"
},
"social": {
".validate": "newData.isBoolean()"
},
"$other": {
".validate": "false"
}
}
},
// Disallow all other attributes.
"$other": {
".validate": false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment