0

This must be something easy to be done, and I'm doing something stupid, but for 4 hours I can't understand what I'm doing wrong. I have databse entity like this:

posts:
    -KK-3ajDuSUglnMMdXgy
       authorId: "DYI4TPbwQocFsVROStUqcGNFHhW2"
       authorName: "john"
       privacy: "PrivacyTypeWorld"
       text: "some post text"

Post can have 3 levels of privacy: for all (PrivacyTypeWorld), only for author (PrivacyTypePrivate), and for friends (friendId, friendId, ...).

My security rules look like this:

{
  "rules": {
     "posts": {
        ".read": "auth != null && (data.child('privacy').val() === 'PrivacyTypeWorld')",
        ".write": "auth != null"
      }
   }
}

But with this rules I see nothing. If I set ".read": "auth != null" - all works fine. What I'm doing wrong?

Edit:

To retrieve data I use:

_refHandle = [[_ref child:@"posts"] observeEventType:FIRDataEventTypeChildAdded
                                      withBlock:^(FIRDataSnapshot *snapshot) {
                [_arrPosts addObject:snapshot];
                [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:_arrPosts.count-1 inSection:0]] withRowAnimation: UITableViewRowAnimationAutomatic];
          }];
daleijn
  • 718
  • 13
  • 22
  • You're missing a level in your rules. To access each node, you need a variable... in this case $post_id for example, then you can reference (speaking generically) $post_id/privacy = 'whatever'. See [Secure Your Data](https://firebase.google.com/docs/database/security/securing-data) about 1/2 way down in the Structuring Your Rules section. Otherwise you are looking for posts/privacy where is should be posts/post_id/privacy. – Jay Jun 11 '16 at 16:31
  • Can you add the code that is not working to your question? – Frank van Puffelen Jun 11 '16 at 17:00
  • @FrankvanPuffelen, code of what? I can see in Firebase console that data actually added (https://www.dropbox.com/s/shbfrob1af8o405/%D0%A1%D0%BA%D1%80%D0%B8%D0%BD%D1%88%D0%BE%D1%82%202016-06-11%2023.03.07.png?dl=0) and rules loojs like in adolfosrs answer – daleijn Jun 11 '16 at 17:02
  • @daleijn he means your swift code. how are you trying to retrieve the data... – adolfosrs Jun 11 '16 at 17:32
  • @adolfosrs, updated question – daleijn Jun 11 '16 at 17:44

2 Answers2

1

You have two mistakes:

  1. your rules define access on the wrong level (as @adolfosrs says). It's easiest to understand by realizing that there is no node posts/privacy, so that check will always return false. Instead you should grant access on posts/$postid, so that you're checking posts/$postid/privacy.

  2. you are trying to read posts. Firebase queries always check the permission on the level where you read/query. Since you're not granting read permission on posts, the query will fail.

This last bit is commonly known as "rules are not filters" or "rules cannot be used to filter data". The best answer about that is Restricting child/field access with security rules.

The easiest solution in your case seems to put the world-readable posts under a separate top-level node where you grant everyone read access:

posts:
    -KK-3ajDuSUglnMMdXgy
       authorId: "DYIciaiew98acohzxvx09qw"
       authorName: "puf"
       text: "Only authenticated users can write this"
publicPosts:
    -KK-3ajDuSUglnMMdXgy
       authorId: "DYI4TPbwQocFsVROStUqcGNFHhW2"
       authorName: "john"
       text: "Everyone can read this"

This these rules:

{
  "rules": {
     "posts": {
        ".read": "auth != null",
        ".write": "auth != null"
      }
     "publicPosts": {
        ".read": true,
        ".write": "auth != null"
      }
   }
}
Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • And in case when privacy sets only to some friends (friendId, friendId, ...). How can I implement this? And why @adolfosrs's answer didn't work? I have to change something in my database structure or what? – daleijn Jun 12 '16 at 06:11
  • But if I set this rule (like in @adolfosrs answer) to .write (only change data -> newData) - this works as expected and I can only add post if 'privacy' === 'PrivacyTypeWorld'. Why this doesn't works for .read? – daleijn Jun 12 '16 at 06:45
  • When you write, you're writing a specific post (`/posts/-KK-3ajDuSUglnMMdXgy`). But when you're querying, you're reading from the list (`/posts`) where you don't have read permission. – Frank van Puffelen Jun 12 '16 at 15:24
  • If each user has access to different posts, you'll have to keep a list of posts (or post ids) that each user has access to. This is quite normal in NoSQL data modeling. I highly recommend spending some time reading [this article on the topic](https://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/). – Frank van Puffelen Jun 12 '16 at 15:25
  • qualifier "data" not for this case (check data that already in database)? – daleijn Jun 12 '16 at 15:31
0

You are not getting into each post. You are looking into privacy inside de posts instead of posts/postId.

In the following rules $post is referring to the post id. So you could also work with the post id using the $post in your read or write if you want to work with any other validation.

{
  "rules": {
     "posts": {
        "$post": {
            ".read": "auth != null && (data.child('privacy').val() === 'PrivacyTypeWorld')",
            ".write": "auth != null"
        }
      }
   }
}
adolfosrs
  • 9,286
  • 5
  • 39
  • 67