最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

firebase - Firestore rules related with User Custom Claims - Stack Overflow

programmeradmin8浏览0评论

I'm having issues on some conditions on my firestore rules. On my use case I have 2 types of users with different custom claims:

  • One user has customerId and sites (array)
  • Other user has customerIds

My plan is to give the following accesses:

  • Allow queries to Customers/{customerId} and the nested collections if either:

    • User has custom claims customerId == customerId (request.auth.token.customerId == customerId)
    • User custom claims customerIds contains customerId (request.auth.token.customerIds.hasAny([customerId])
  • Allow queries to Sites/{siteId} and the nested collections if either:

    • User custom claims sites contains {siteId} and the variable of the document Sites⁄{sites} permissionsToView (array) must contain the user custom claims customerId.
    • The variable of the document Sites⁄{sites} permissionsToView (array) must contain the user custom claims customerId.

For the first permission i made the following conditions:

match /Customers/{customerId}/{document=**} {
      allow read, write: if isCustomerAccessible(customerId);
    }
function isCustomerAccessible(customerId) {
      return 
        request.auth.token.customerIds.hasAny([customerId]) ||
        request.auth.token.customerId == customerId ||
        request.auth.token.isSuper;
    }

Getting the specific permitted document from the client-side its possible, but doing a query that is going to retrieve allowed documents It returns "permission-denied". The Objective to allow the collection independent of the query that is running.

For the second condition i made the following:

match /Sites/{site}/{document=**} {
      allow read: if isSiteAccessibleView(site);
      allow write: if isSiteAccessibleEdit(site);
    }

function isSiteAccessibleView(site) {
      return request.auth != null && (
        request.auth.token.isSuper ||
        hasSiteViewPermission(site) ||
        hasSitePermissionThroughCustomerIds(site)
      );
    }
    
    function isSiteAccessibleEdit(site) {
      return request.auth != null && (
        request.auth.token.isSuper ||
        hasSiteEditPermission(site) ||
        hasSitePermissionThroughCustomerIds(site)
      );
    }

    function hasSiteViewPermission(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToView;
      return permissionValue.hasAny([request.auth.token.customerId]);
    }
    
    function hasSiteEditPermission(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToEdit;
      let sites = request.auth.token.sites;
      return permissionValue.hasAny([request.auth.token.customerId]) && sites.hasAny([site]);
    }

    function hasSitePermissionThroughCustomerIds(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToEdit;
      return request.auth.token.customerIds.hasAny(permissionValue);
    }

Both conditions should give access to all documents of the nested collections however getting the collection Customer/{customerId}/People with a user that has the correct customerId isn't allowed.

To reproduce use the following rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Customers/{customerId}/{document=**} {
      allow read, write: if isCustomerAccessible(customerId);
    }
        function isCustomerAccessible(customerId) {
      return 
        request.auth.token.customerIds.hasAny([customerId]) ||
        request.auth.token.customerId == customerId ||
        request.auth.token.isSuper;
    }
  }
}

use a client side and signIn with a user. This user must have the variable customerId inside. When doing a query to the collection Customers (that is going to only return allowed documents) it returns permissions denied

const customersCollection = collection(db, "Customers")
    const customersQuery = query(
        customersCollection,
        where("tenantID", "==", tenantID)
    )
    const docs = await getDocs(customersQuery)

I'm having issues on some conditions on my firestore rules. On my use case I have 2 types of users with different custom claims:

  • One user has customerId and sites (array)
  • Other user has customerIds

My plan is to give the following accesses:

  • Allow queries to Customers/{customerId} and the nested collections if either:

    • User has custom claims customerId == customerId (request.auth.token.customerId == customerId)
    • User custom claims customerIds contains customerId (request.auth.token.customerIds.hasAny([customerId])
  • Allow queries to Sites/{siteId} and the nested collections if either:

    • User custom claims sites contains {siteId} and the variable of the document Sites⁄{sites} permissionsToView (array) must contain the user custom claims customerId.
    • The variable of the document Sites⁄{sites} permissionsToView (array) must contain the user custom claims customerId.

For the first permission i made the following conditions:

match /Customers/{customerId}/{document=**} {
      allow read, write: if isCustomerAccessible(customerId);
    }
function isCustomerAccessible(customerId) {
      return 
        request.auth.token.customerIds.hasAny([customerId]) ||
        request.auth.token.customerId == customerId ||
        request.auth.token.isSuper;
    }

Getting the specific permitted document from the client-side its possible, but doing a query that is going to retrieve allowed documents It returns "permission-denied". The Objective to allow the collection independent of the query that is running.

For the second condition i made the following:

match /Sites/{site}/{document=**} {
      allow read: if isSiteAccessibleView(site);
      allow write: if isSiteAccessibleEdit(site);
    }

function isSiteAccessibleView(site) {
      return request.auth != null && (
        request.auth.token.isSuper ||
        hasSiteViewPermission(site) ||
        hasSitePermissionThroughCustomerIds(site)
      );
    }
    
    function isSiteAccessibleEdit(site) {
      return request.auth != null && (
        request.auth.token.isSuper ||
        hasSiteEditPermission(site) ||
        hasSitePermissionThroughCustomerIds(site)
      );
    }

    function hasSiteViewPermission(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToView;
      return permissionValue.hasAny([request.auth.token.customerId]);
    }
    
    function hasSiteEditPermission(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToEdit;
      let sites = request.auth.token.sites;
      return permissionValue.hasAny([request.auth.token.customerId]) && sites.hasAny([site]);
    }

    function hasSitePermissionThroughCustomerIds(site) {
      let siteDoc = get(/databases/$(database)/documents/Sites/$(site));
      let permissionValue = siteDoc.data.permissionsToEdit;
      return request.auth.token.customerIds.hasAny(permissionValue);
    }

Both conditions should give access to all documents of the nested collections however getting the collection Customer/{customerId}/People with a user that has the correct customerId isn't allowed.

To reproduce use the following rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Customers/{customerId}/{document=**} {
      allow read, write: if isCustomerAccessible(customerId);
    }
        function isCustomerAccessible(customerId) {
      return 
        request.auth.token.customerIds.hasAny([customerId]) ||
        request.auth.token.customerId == customerId ||
        request.auth.token.isSuper;
    }
  }
}

use a client side and signIn with a user. This user must have the variable customerId inside. When doing a query to the collection Customers (that is going to only return allowed documents) it returns permissions denied

const customersCollection = collection(db, "Customers")
    const customersQuery = query(
        customersCollection,
        where("tenantID", "==", tenantID)
    )
    const docs = await getDocs(customersQuery)
Share Improve this question edited Mar 28 at 15:21 AndreCoelhoo asked Mar 28 at 14:28 AndreCoelhooAndreCoelhoo 236 bronze badges 4
  • 2 Security rules by themselves don't do anything. They must be paired with specific query code and data to be meaningful. Please edit the question to include all relevant information that anyone can use to reconstruct the scenario you're describing, and observe the unexpected behavior on their own. – Doug Stevenson Commented Mar 28 at 14:47
  • So its not possible to make conditions on the rules based on the user only? Since logically should work, since the condition is always true for that user. – AndreCoelhoo Commented Mar 28 at 14:49
  • I haven't made any claims about what's possible. I'm asking for relevant details so we can see what the problem is or what you might be doing wrong. Please edit the question to include your minimal complete reproducible example. – Doug Stevenson Commented Mar 28 at 14:52
  • 1 I'm with Doug: without seeing the query that fails against these rules (and all relevant data that the rules check for), it's hard to be certain. That said, I expect you're not passing the data filter that your rules require in siteDoc.data.permissionsToEdit. If that's the case (re)study the documentation on rules not actually filtering the data: firebase.google/docs/firestore/security/… – Frank van Puffelen Commented Mar 28 at 15:11
Add a comment  | 

1 Answer 1

Reset to default 1

Your rules don't match your query. Your rules only allow access to documents in the "Customers" collection which have an ID that must be in their custom claims. That means a client query absolutely must call out that ID in a get of a single document. A collection query won't work.

Your query is actually requesting all of the documents in "Customers" filtered by a document field "tenantID". It's not limiting the query based on the document ID that is required by your rules, so the rules simply reject the query because it's potentially asking for more than what's allowed.

If your rules accurately describe the access to be given, your client code will need to request only single documents whose IDs are in the user's custom claims. You can't simply ask for everything and expect the rules to filter the results for you. Security rules are not filters (you should definitely read and understand what that doc is say).

An alternative might be to put the customerId in a document field, and use that field as a filter in the client client query. Your rules should also then check the contents of that field instead of the document ID.

发布评论

评论列表(0)

  1. 暂无评论