An issue with preventing users from changing specific document fields in Firestore










2















I'm using firebase in my Android project, I have a users collection in the Cloud Firestore database, users can only update some fields, but can read all, I know it is not possible to protect a subset of a document from being updated, and security rules can be applied only to the entire document, so I searched about this and the only solution I found is to make a sub-collection inside the user document and create a new document inside this sub-collection and finally put fields I want to protect in there, then I can easily secure this document applying this code in security rules section:



match /users/userId/securedData/securedData 
allow write: if false;
allow read: if true;



So now no one can write these fields, but anyone can read, and this is exactly what I want, but later I found that I need to query users based on fields inside the sub-collection, what is known as a collection group query which is not supported at the moment, so I ended up with two solutions:



  1. Retrieve all users and filter them at the client side, but this will increase the number of the document reads because I'm querying all users collection documents + an extra sub-collection document read for each user to get the needed field for filtering users.


  2. Instead of making a sub-collection inside user document and incurring these problems, just keep all fields in the top level document(user document), allowing users to update any field.



    match /users/userId 
    allow update, read: if true;
    allow create, remove: if false;



    But make an (onUpdate) cloud function to listen on user document update and
    detect changes in fields which aren't allowed to be modified by the user, so
    if the user tried to change these fields I can detect this and return modified fields to their previous values like this:



    export const updateUser = functions.firestore
    .document('users/userId')
    .onUpdate((change, context) =>
    const previousValue = change.before.data().securedField;
    const newValue = change.after.data().securedField;

    if (previousValue !== newValue)
    return db.collection('users')
    .doc(context.params.userId)
    .update( securedField: previousValue );

    );


Is the second solution secure? which is the best solution for this? or any other solutions?










share|improve this question




























    2















    I'm using firebase in my Android project, I have a users collection in the Cloud Firestore database, users can only update some fields, but can read all, I know it is not possible to protect a subset of a document from being updated, and security rules can be applied only to the entire document, so I searched about this and the only solution I found is to make a sub-collection inside the user document and create a new document inside this sub-collection and finally put fields I want to protect in there, then I can easily secure this document applying this code in security rules section:



    match /users/userId/securedData/securedData 
    allow write: if false;
    allow read: if true;



    So now no one can write these fields, but anyone can read, and this is exactly what I want, but later I found that I need to query users based on fields inside the sub-collection, what is known as a collection group query which is not supported at the moment, so I ended up with two solutions:



    1. Retrieve all users and filter them at the client side, but this will increase the number of the document reads because I'm querying all users collection documents + an extra sub-collection document read for each user to get the needed field for filtering users.


    2. Instead of making a sub-collection inside user document and incurring these problems, just keep all fields in the top level document(user document), allowing users to update any field.



      match /users/userId 
      allow update, read: if true;
      allow create, remove: if false;



      But make an (onUpdate) cloud function to listen on user document update and
      detect changes in fields which aren't allowed to be modified by the user, so
      if the user tried to change these fields I can detect this and return modified fields to their previous values like this:



      export const updateUser = functions.firestore
      .document('users/userId')
      .onUpdate((change, context) =>
      const previousValue = change.before.data().securedField;
      const newValue = change.after.data().securedField;

      if (previousValue !== newValue)
      return db.collection('users')
      .doc(context.params.userId)
      .update( securedField: previousValue );

      );


    Is the second solution secure? which is the best solution for this? or any other solutions?










    share|improve this question


























      2












      2








      2


      0






      I'm using firebase in my Android project, I have a users collection in the Cloud Firestore database, users can only update some fields, but can read all, I know it is not possible to protect a subset of a document from being updated, and security rules can be applied only to the entire document, so I searched about this and the only solution I found is to make a sub-collection inside the user document and create a new document inside this sub-collection and finally put fields I want to protect in there, then I can easily secure this document applying this code in security rules section:



      match /users/userId/securedData/securedData 
      allow write: if false;
      allow read: if true;



      So now no one can write these fields, but anyone can read, and this is exactly what I want, but later I found that I need to query users based on fields inside the sub-collection, what is known as a collection group query which is not supported at the moment, so I ended up with two solutions:



      1. Retrieve all users and filter them at the client side, but this will increase the number of the document reads because I'm querying all users collection documents + an extra sub-collection document read for each user to get the needed field for filtering users.


      2. Instead of making a sub-collection inside user document and incurring these problems, just keep all fields in the top level document(user document), allowing users to update any field.



        match /users/userId 
        allow update, read: if true;
        allow create, remove: if false;



        But make an (onUpdate) cloud function to listen on user document update and
        detect changes in fields which aren't allowed to be modified by the user, so
        if the user tried to change these fields I can detect this and return modified fields to their previous values like this:



        export const updateUser = functions.firestore
        .document('users/userId')
        .onUpdate((change, context) =>
        const previousValue = change.before.data().securedField;
        const newValue = change.after.data().securedField;

        if (previousValue !== newValue)
        return db.collection('users')
        .doc(context.params.userId)
        .update( securedField: previousValue );

        );


      Is the second solution secure? which is the best solution for this? or any other solutions?










      share|improve this question
















      I'm using firebase in my Android project, I have a users collection in the Cloud Firestore database, users can only update some fields, but can read all, I know it is not possible to protect a subset of a document from being updated, and security rules can be applied only to the entire document, so I searched about this and the only solution I found is to make a sub-collection inside the user document and create a new document inside this sub-collection and finally put fields I want to protect in there, then I can easily secure this document applying this code in security rules section:



      match /users/userId/securedData/securedData 
      allow write: if false;
      allow read: if true;



      So now no one can write these fields, but anyone can read, and this is exactly what I want, but later I found that I need to query users based on fields inside the sub-collection, what is known as a collection group query which is not supported at the moment, so I ended up with two solutions:



      1. Retrieve all users and filter them at the client side, but this will increase the number of the document reads because I'm querying all users collection documents + an extra sub-collection document read for each user to get the needed field for filtering users.


      2. Instead of making a sub-collection inside user document and incurring these problems, just keep all fields in the top level document(user document), allowing users to update any field.



        match /users/userId 
        allow update, read: if true;
        allow create, remove: if false;



        But make an (onUpdate) cloud function to listen on user document update and
        detect changes in fields which aren't allowed to be modified by the user, so
        if the user tried to change these fields I can detect this and return modified fields to their previous values like this:



        export const updateUser = functions.firestore
        .document('users/userId')
        .onUpdate((change, context) =>
        const previousValue = change.before.data().securedField;
        const newValue = change.after.data().securedField;

        if (previousValue !== newValue)
        return db.collection('users')
        .doc(context.params.userId)
        .update( securedField: previousValue );

        );


      Is the second solution secure? which is the best solution for this? or any other solutions?







      firebase google-cloud-firestore google-cloud-functions firebase-security-rules






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Dec 6 '18 at 22:53







      Rabia Hamouda

















      asked Nov 14 '18 at 13:55









      Rabia HamoudaRabia Hamouda

      133




      133






















          1 Answer
          1






          active

          oldest

          votes


















          2














          Your approach is definitely a possibility, and an interesting use of Cloud Functions. But there will be a delay between the write operation from the user, and the moment the Cloud Function detects and reverts the change.



          I'd probably catch the situation in security rules. While you can't deny the user from writing the field in security rules, you can ensure that they can only write the same value to a field that currently has. That effectively also makes it impossible for them to change the value of a field. You do this by:



          allow write: if request.resource.data.securedField == resource.data.securedField;


          This rule ensures that the field in the updated document (request.resource.data.securedField) has the same value as that field in the current document(resource.data.securedField).






          share|improve this answer























          • Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

            – Rabia Hamouda
            Nov 14 '18 at 15:55











          Your Answer






          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "1"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53301895%2fan-issue-with-preventing-users-from-changing-specific-document-fields-in-firesto%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          Your approach is definitely a possibility, and an interesting use of Cloud Functions. But there will be a delay between the write operation from the user, and the moment the Cloud Function detects and reverts the change.



          I'd probably catch the situation in security rules. While you can't deny the user from writing the field in security rules, you can ensure that they can only write the same value to a field that currently has. That effectively also makes it impossible for them to change the value of a field. You do this by:



          allow write: if request.resource.data.securedField == resource.data.securedField;


          This rule ensures that the field in the updated document (request.resource.data.securedField) has the same value as that field in the current document(resource.data.securedField).






          share|improve this answer























          • Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

            – Rabia Hamouda
            Nov 14 '18 at 15:55
















          2














          Your approach is definitely a possibility, and an interesting use of Cloud Functions. But there will be a delay between the write operation from the user, and the moment the Cloud Function detects and reverts the change.



          I'd probably catch the situation in security rules. While you can't deny the user from writing the field in security rules, you can ensure that they can only write the same value to a field that currently has. That effectively also makes it impossible for them to change the value of a field. You do this by:



          allow write: if request.resource.data.securedField == resource.data.securedField;


          This rule ensures that the field in the updated document (request.resource.data.securedField) has the same value as that field in the current document(resource.data.securedField).






          share|improve this answer























          • Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

            – Rabia Hamouda
            Nov 14 '18 at 15:55














          2












          2








          2







          Your approach is definitely a possibility, and an interesting use of Cloud Functions. But there will be a delay between the write operation from the user, and the moment the Cloud Function detects and reverts the change.



          I'd probably catch the situation in security rules. While you can't deny the user from writing the field in security rules, you can ensure that they can only write the same value to a field that currently has. That effectively also makes it impossible for them to change the value of a field. You do this by:



          allow write: if request.resource.data.securedField == resource.data.securedField;


          This rule ensures that the field in the updated document (request.resource.data.securedField) has the same value as that field in the current document(resource.data.securedField).






          share|improve this answer













          Your approach is definitely a possibility, and an interesting use of Cloud Functions. But there will be a delay between the write operation from the user, and the moment the Cloud Function detects and reverts the change.



          I'd probably catch the situation in security rules. While you can't deny the user from writing the field in security rules, you can ensure that they can only write the same value to a field that currently has. That effectively also makes it impossible for them to change the value of a field. You do this by:



          allow write: if request.resource.data.securedField == resource.data.securedField;


          This rule ensures that the field in the updated document (request.resource.data.securedField) has the same value as that field in the current document(resource.data.securedField).







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 14 '18 at 14:11









          Frank van PuffelenFrank van Puffelen

          234k29380407




          234k29380407












          • Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

            – Rabia Hamouda
            Nov 14 '18 at 15:55


















          • Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

            – Rabia Hamouda
            Nov 14 '18 at 15:55

















          Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

          – Rabia Hamouda
          Nov 14 '18 at 15:55






          Thank you very much! this worked for me, you really helped me a lot, actually I have read this in the firebase docs before, but it didn't occur to me that it would be used here.

          – Rabia Hamouda
          Nov 14 '18 at 15:55


















          draft saved

          draft discarded
















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53301895%2fan-issue-with-preventing-users-from-changing-specific-document-fields-in-firesto%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          27

          Top Tejano songwriter Luis Silva dead of heart attack at 64

          Category:Rhetoric