Firestore Security Rules: Allow User To Create Doc Only If New Doc ID is same as User ID

前端 未结 3 604
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-01 02:02

When users log in for the first time, I need to also call a function that creates a document in my firestore users collection to store their profile data. Using Web SDK.

相关标签:
3条回答
  • 2021-01-01 02:41

    Update

    I recently had to update my rule set because of some changes to the way firestore rules worked, and changes in how the "getAfter" function works. Specifically, I am now able to use request.resource for data comarisons. Anyways, it appears that I can accomplish my goals with simpler rules now so I thought I'd update this answer and share.

    Goals

    • User can create a document, only if the new document ID matches their user ID.
    • User cannot declare themselves an "admin", block create / update / write requests if "admin" is a field (unless they are already an admin)
    service cloud.firestore {
      match /databases/{database}/documents {
      
        // Allow users to create a document for themselves in the users collection
        match /users/{document=**} {
          allow create: if request.resource.id == request.auth.uid &&
            !("admin" in request.resource.data);
        }
        
        // Allow users to read, write, update documents that have the same ID as their user id
        match /users/{userId} {   
            // Allow users to read their own profile (doc id same as user id)
          allow read: if request.auth.uid == userId;
          
          // Allow users to write / update their own profile as long as no "admin"
          // field is trying to be added or created - unless they are already an admin
          allow write, update: if request.auth.uid == userId &&
            (
              !("admin" in request.resource.data) ||
              get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true // allow admin to update their own profile
            )
    
          // Allow users to read their own feeds
          match /feeds/{document=**} {
            allow read: if request.auth.uid == userId;
          } 
        }
      }
    }
    

    Old Answer

    So I figured out how to do this in a workaround way. I also had some additional write / update conditions that prevent the user from changing their permission level. This was for some reason, preventing any "creates" from happening. So I had to mirror the same conditions in create, and the write / update rules. For some reason this was necessary.

    This new rule structure accomplishes the following

    First Section, for create rule

    • allows the only authenticated users to create documents only in the "users" collection (during the user setup process, a document is created automatically with the same ID as their user id).
    • does not allow creation of a document containing the "admin" field, which would suggest they are trying to gain admin access.
    • it seems that validating the id of the document during creation is not possible, hence additional write / update rules below

    Second Section - read, update, write

    • allows users to read / write / update only documents that have the same ID as their user id (user trying to create a document with an ID other than their user id will fail, also prevents the user from spamming creation of tons of docs by manipulating the client-side JS request.)
    • does not allow users to write / update their profile to include the "admin" field

    Rules

    service cloud.firestore {
    
          match /databases/{database}/documents {
          
            // Allow users to create documents in the user's collection
            match /users/{document=**} {
              allow create: if request.auth.uid != null &&
                !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
            }
            
            // Allow users to read, write, update documents that have the same ID as their user id
            match /users/{userId} {   
                // Allow users to read their own profile (doc id same as user id)
              allow read: if request.auth.uid == userId;
              
              // Allow users to write / update their own profile as long as no "admin" field is trying to be added or created
              allow write, update: if request.auth.uid == userId &&
                !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
            }
          }
        }
    

    PS This was not intuitive at all, so if someone has a better workaround, please post it. Also, I'm really hoping that once firestore 1.0 is out, it will bring with it some huge improvements to rules and rule documentation.

    0 讨论(0)
  • 2021-01-01 02:49

    The solution i came up with. My tests showed it's not possible to create other user-docs than the own uid and it prevents normal users to change any admin state.

    
    
        rules_version = '2';
        service cloud.firestore {
          match /databases/{database}/documents {
    
            function isAdmin() {
              return get(/databases/$(database)/documents/users/$(request.auth.uid)).isAdmin == true ||
                     get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
            }
            function signedIn(){
                return request.auth.uid != null;
            }
    
            match /users/{user} {
    
              // allow updates to own user-doc
              allow read, update, delete: if request.auth.uid == user &&
    
                // allow updates to own user-doc if "isAdmin" field is the same as before the update (in case user was already admin)
                (request.resource.data.isAdmin == resource.data.isAdmin ||
    
                    // or allow updates if "isAdmin" will be set to false
                    request.resource.data.isAdmin == false ||
    
                    // or allow updates if no "isAdmin" field exists after the update
                    !("isAdmin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data)
                );
    
              // allow creation of user-doc with own uid and no others       
              allow create: if request.auth.uid == user &&
    
                // if no "isAdmin" field is set
                !("isAdmin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
    
              // give full access to admins
              allow read, write: if isAdmin();
            }
          }
        }
    
    
    0 讨论(0)
  • 2021-01-01 02:56

    A little bit late, but I manage to tweak one of your possible solutions and make it work:

    allow create: if path("/databases/(default)/documents/users/" + request.auth.uid) == request.path;
    

    Just had to replace the database variable with (default). Yes, not fancy...

    0 讨论(0)
提交回复
热议问题