title: Firestore Security Rules - How can I check that a field is/isn't being modified?
body: <p>For the life of me, I cannot understand why the following is resulting in a <code>false</code> for allowing writes. Assume my <code>users</code> collection is empty to start, and I am writing a document of the following form from my Angular frontend:</p>
  
  <pre><code>{
    displayName: 'FooBar',
    email: 'foo.bar@example.com'
  }
  </code></pre>
  
  <p>My current security rules:</p>
  
  <pre><code>service cloud.firestore {
    match /databases/{database}/documents {
      match /users/{userId} {
        function isAdmin() {
          return resource.data.role == 'ADMIN';
        }
  
        function isEditingRole() {
          return request.resource.data.role != null;
        }
  
        function isEditingOwnRole() {
          return isOwnDocument() &amp;&amp; isEditingRole();
        }
  
        function isOwnDocument() {
          return request.auth.uid == userId;
        }
  
        allow read: if isOwnDocument() || isAdmin();
        allow write: if !isEditingOwnRole() &amp;&amp; (isOwnDocument() || isAdmin());
      }
    }
  }
  </code></pre>
  
  <p>In general, I want no users to be able to edit their own role. Regular users can edit their own document otherwise, and admins can edit anyone's.</p>
  
  <p>Stubbing <code>isEditingRole()</code> for <code>false</code> gives the expected result, so I've narrowed it down to that expression.</p>
  
  <p>The write keeps coming back false, and I cannot determine why. Any ideas or fixes would be helpful!</p>
  
  <p><strong>Edit 1</strong></p>
  
  <p>Things I've tried:</p>
  
  <pre><code>function isEditingRole() {
    return request.resource.data.keys().hasAny(['role']);
  }
  </code></pre>
  
  <p>and</p>
  
  <pre><code>function isEditingRole() {
    return 'role' in request.resource.data;
  }
  </code></pre>
  
  <p>and</p>
  
  <pre><code>function isEditingRole() {
    return 'role' in request.resource.data.keys();
  }
  </code></pre>
  
  <p><strong>Edit 2</strong></p>
  
  <p>Note that eventually, admins will set a role for users, so a role could eventually exist on a document. This means that, according to the <a href="https://firebase.google.com/docs/firestore/reference/security/#request" rel="noreferrer">Firestore docs</a> below, the request will have a <code>role</code> key, even if wasn't in the original request.</p>
  
  <blockquote>
    <p>Fields not provided in the request which exist in the resource are added to <code>request.resource.data</code>. Rules can test whether a field is modified by comparing <code>request.resource.data.foo</code> to <code>resource.data.foo</code> knowing that every field in the <code>resource</code> will also be present in <code>request.resource</code> even if it was not submitted in the write request.</p>
  </blockquote>
  
  <p>According to that, I think the three options from "Edit 1" are ruled out. I did try the suggestion of <code>request.resource.data.role != resource.data.role</code> and that's not working either... I'm at a loss and am beginning to wonder if there's actually a bug in Firestore.</p>