TomGrill Games
TomGrill Games

Reputation: 1593

Firebase multi-location rules - same value

This is what I want at the end:

{
  "data" : {
    "account" : {
      "K1472290187836" : {
        "created" : 1472290190043,
        "id" : "K1472290187836"
      }
    },
    "auth" : {
      "d182ddec-f1c7-41c5-8b0e-198bfb5d9efe" : {
        "account_id" : "K1472290187836",
        "active" : true,
        "created" : 1472290190043,
        "id" : "d182ddec-f1c7-41c5-8b0e-198bfb5d9efe"
      }
    }
  }
}

This are my classes for the data:

public class Account {
    public long created;
    public String id;

    public HashMap<String, Object> toMap() {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("id", id);
        map.put("created", ServerValue.TIMESTAMP);
        return map;
    }
}

public class Auth {
    public String id;
    public long created;
    public boolean active;
    public String account_id;

    public HashMap<String, Object> toMap() {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("id", id);
        map.put("created", ServerValue.TIMESTAMP);
        map.put("active", active);
        map.put("account_id", account_id);
        return map;
    }
}

This is my multi-location update:

final Auth auth = new Auth();
auth.id = FirebaseAuth().getCurrentUser().getUid();
auth.active = true;
auth.account_id = "K" + System.currentTimeMillis(); // TODO replace with proper id generator

final Account account = new Account();
account.id = auth.account_id;

HashMap<String, Object> newUserMap = new HashMap<String, Object>();
newUserMap.put("/data/account/" + account.id, account.toMap());
newUserMap.put("/data/auth/" + auth.id, auth.toMap());


FirebaseDatabase().getReference().updateChildren(newUserMap);

What I need are the rules which validate that data/account/$account_id/id has the same value as /data/auth/$auth_id/account_id before data can be stored.

Upvotes: 1

Views: 422

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598728

Trimming the data back to the minimum, it seems like you want:

{
  "data" : {
    "account" : {
      "K1472290187836" : {
        "id" : "K1472290187836"
      }
    },
    "auth" : {
      "d182ddec-f1c7-41c5-8b0e-198bfb5d9efe" : {
        "account_id" : "K1472290187836",
      }
    }
  }
}

This is probably a good start:

{
  "rules": {
    "data": {
      "account": {
        "$id": {
          "id": {
            ".validate": "newData.val() == $id"
          }
        }
      },
      "auth": {
        "$uid": {
          ".write": "$uid === auth.uid",
          "account_id": {
            ".validate": "
newData.parent().parent().child('account').child(newData.val()).exists() &&
newData.parent().parent().child('account').child(newData.val()).child('id').val() === newData.val()"
          }
        }
      }
    }
  }
}

Since root and data refer to the data as it exists before the write operation, the above uses newData, which refers to the data as it will exist after the write operation (if that operation succeeds).

You don't really need the double condition on the account_id validation, since the second one duplicates the functionality of the first. But since I wasn't sure whether you want to validate the account key or the value of the id property, I added both conditions for easy copy/paste/remove-the-one-you-don't-care-about.

I recommend reconsidering if you really need to store the ID twice in that fragment. Data duplication is common, but in this case I don't see a lot of value gain (and it leads to considerations like the one above: which is leading?).

Upvotes: 2

Related Questions