Firebase数据库规则-允许未经身份验证的用户仅过滤查询

时间:2019-11-10 17:08:36

标签: javascript firebase firebase-realtime-database firebase-security

我有一个Google Firestore实时数据库,其中包含标志isPublic。如果设置为true,则未经身份验证的用户可以读取子节点details。如果设置为false,则该记录只能由一个(目前)经过身份验证和授权的用户访问。

授权用户是唯一具有写许可权的人。

此外,子节点private始终仅向一个授权用户授予访问权限。

我研究过的解决方案基于基于查询的规则https://firebase.google.com/docs/database/security/securing-data#query-based_rules 不幸的是,即使是授权用户,我也收到permission_denied的回复。

除了信息:这是在使用Firebase SDK的Vue JS项目中。

这是我的数据库示例。

第一个产品应该对未经身份验证的用户具有对details的读取权限,但对private则没有读取权限。

通过强制用户运行查询dbRef.orderByChild('isPublic').equalTo(true)

,根本不应该向未经身份验证的用户显示第二个用户
"products": {
  "-pushIdxyz1" : {
    "isPublic" : true,

    "private" : {
      "notes" : "lorem ipsum always private",
      "soldDate" : ""
    },

    "details" : {
      "imageURL" : "https://firebasestorage.imagexyz",
      "keywords" : "this, that",
      "title" : "Public product title",
      "workCategory" : "hardware",
    },
  },
  "-pushIdxyz2" : {
    "isPublic" : false,

    "private" : {
      "notes" : "lorem ipsum always private",
      "soldDate" : ""
    },

    "details" : {
      "imageURL" : "https://firebasestorage.imagexyz",
      "keywords" : "this, that",
      "title" : "Secret product title",
      "workCategory" : "hardware",
    },
  }
}

我的无效规则

"rules": {
  "products": {
    "$product_id": {
      ".indexOn": "isPublic",
      ".read": "query.equalTo == true || auth.uid == '[admin user id]'"
      // alternative attempt
      // ".read": "(query.orderByChild == 'isPublic' && query.equalTo == true) || auth.uid == '[admin user id]'",
      ,
      ".write": "auth.uid == '[admin user id]'",

      "details": {
        ".read": true,
      },
      "private": {
        ".read": "auth.uid == '[admin user id]'",
      }
    }
  }
}

对于未经授权的用户,此查询应授予对明细子节点的读取访问权限:

firebase.database().ref('products').orderByChild('isPublic').equalTo(true).once('value')
  .then(...)

此查询(仅适用于授权用户)应授予对所有数据的读取权限

firebase.database().ref('products').once('value')
  .then(...)

在当前设置下,无论是否以授权用户身份登录,我都对查询“权限被拒绝”。

2 个答案:

答案 0 :(得分:1)

如果希望用户能够从products节点进行读取,则需要在该节点上具有.read规则。并且由于您只想在对公共产品查询时才允许读取,因此您应该验证该特定查询。

类似这样的东西:

"rules": {
  "products": {
    ".indexOn": "isPublic",
    ".read": "(query.orderBy == 'isPublic' && query.equalTo == true)
              || auth.uid == '[admin user id]'"
  }
}

所以:

  1. 您将规则定义的级别太低,这意味着对/products的所有读取均被拒绝。
  2. 您的规则未选中orderBy字段。

请注意,您在.read上声明的附加private规则在这里是没有意义的,因为无论如何,同一用户已经获得了整个`products节点的读取权限。

答案 1 :(得分:0)

Franks答案将我引向正确的方向(谢谢!),但我决定自己发布一个更完整的答案。

这是数据结构。请注意,我将“ isPublic”更改为“ isPublished”。 现在,“产品”包含两个子节点:“私有”和“公共”。两者都使用相同的密钥。

// Firebase Data
"products": {
  "private" : {
    "-pushId_1" : {
      "notes" : "Private notes product 1",
      "soldDate" : ""
    },
    "-pushId_2" : {
      "notes" : "Private notes product 2",
      "soldDate" : ""
    }

  },
  "public" : {
    "-pushId_1" : {
      "imageURL" : "https://firebasestorage.imagexyz",
      "isPublished" : true,
      "title" : "Published product title",
    },
    "-pushId_2" : {
      "imageURL" : "https://firebasestorage.imagexyz",
      "isPublished" : false,
      "title" : "Unpublished product title, only accessible if authorized",
    }
  }
}

该规则允许管理员进行所有操作,并且如果查询查找已发布的产品,则允许未经身份验证的访问公共子节点

// Firebase Rules
{
  "rules": {
    "products" : {
      ".read": "auth.uid == '[admin user id]'",
      ".write": "auth.uid == '[admin user id]'",

      "public" : {
        ".indexOn": "isPublished",
        ".read": "(query.orderByChild == 'isPublished' && query.equalTo == true)"
      }
    }
  }
}

要创建新产品,我首先将其设置为“公开”,然后使用返回的密钥创建私人记录

// Add new product
db.ref('products/public').push(publicData)
  .then((data) => {
    key = data.key
    return key
  })
  .then((key) => {
    db.ref('products/private').child(key).update(privateData)
    return key
  })
  .then( ... )

要获取产品,公共/未经身份验证的用户必须在公共场所寻找isPublished。 管理员可以遍历公共和私有子节点

// Retrieve products - Public
db.ref('products/public').orderByChild('isPublished').equalTo(true).once('value')
  .then((data) => {
    let publicData = data.val()

    // Do something with the data
  })

// Retrieve products - Admin
db.ref('products').once('value')
  .then((data) => {
    let publicData = data.child('public').val()
    let privateData = data.child('private').val()

    // Do something with the data
  })