How to validate array values with firestore's security rules? - firebase

I've a form which creates the following JSON structure.
{
"reviewed":false,
"title":"Just a title",
"user":"UYV9TRKXfNW1NeCyFyfjZfagJ8B",
"items":[
{
"age":"33",
"experience":"Newcomer",
"image":"https://image-url",
"job":"Nerd",
"name":"Testname",
"party":"AAA",
"type":"person"
},
{
"age":"33",
"experience":"Newcomer",
"image":"https://image-url",
"job":"Informatiker",
"name":"Testname",
"party":"AAA",
"type":"person"
}
]
}
How do I check the values of "items" with firestore's security rules? Is there a way to loop/iterate over the array?

For the sake of completeness: That's my solution so far. I did it the way described in the linked answer. The possible amount of items is limited to 10, so we can go without dynamic loops.
service cloud.firestore {
match /databases/{database}/documents {
match /events/{event} {
function isAuthed() {
return request.auth.uid != null
&& request.auth.uid == request.resource.data.user
&& request.auth.token.email_verified == true;
}
function isReviewed() {
return request.resource.data.reviewed == false
|| request.resource.data.reviewed == "false"
}
function isValidTitle() {
return isValidStringInput(request.resource.data.title, 200);
}
function items() {
return request.resource.data.items;
}
function isValidPerson(item) {
return items()[item].keys().hasAll(['image','type','name','job','age','party','experience'])
&& isValidStringInput(items()[item].image, 100)
&& isValidStringInput(items()[item].type, 10)
&& isValidStringInput(items()[item].name, 50)
&& isValidStringInput(items()[item].job, 50)
&& isValidStringInput(items()[item].party, 50)
&& isValidStringInput(items()[item].experience, 50)
&& isValidNumber(items()[item].age);
}
function isValidParty(item) {
return items()[item].keys().hasAll(['image','type','name','orientation','experience','promi'])
&& isValidStringInput(items()[item].image, 100)
&& isValidStringInput(items()[item].type, 10)
&& isValidStringInput(items()[item].name, 50)
&& isValidStringInput(items()[item].orientation, 50)
&& isValidStringInput(items()[item].experience, 50)
&& isValidStringInput(items()[item].promi, 50);
}
function isValidItem(item) {
return isValidPerson(item)
|| isValidParty(item);
}
function isValidStringInput(input, maxSize) {
return input is string
&& input.size() > 0
&& input.size() <= maxSize;
}
function isValidNumber(input) {
return input is int
|| input.matches('^[0-9]+$');
}
// One can READ
// always ...
allow read: if true;
// One can WRITE, when ...
// writer is logged in
// uid in event is same as uid of writer
// writer has email confirmed
// reviewed is initial set to false
// form/user input is ok
allow write, update:
if isAuthed()
&& isReviewed()
&& isValidTitle()
&& items().size() >= 1
&& items().size() <= 10
&& isValidItem(0)
&& (items().size() < 2 || isValidItem(1))
&& (items().size() < 3 || isValidItem(2))
&& (items().size() < 4 || isValidItem(3))
&& (items().size() < 5 || isValidItem(4))
&& (items().size() < 6 || isValidItem(5))
&& (items().size() < 7 || isValidItem(6))
&& (items().size() < 8 || isValidItem(7))
&& (items().size() < 9 || isValidItem(8))
&& (items().size() < 10 || isValidItem(9));
}
}
}

As far as I know. You still can't use loops in firestore security rules and the linked answer and the example is still valid and shows how you can do validations using functions. This could become unusable if the array grows and it might be better to choose another data structure like an own collection for your items.
Cheers,
Lars

Related

how to import or require CSS file conditionally eg(According to IOS version)

checkVersion() {
if (typeof window !== 'undefined' && window && window.navigator && window.navigator !== undefined) {
let agent = window.navigator.userAgent, start = agent.indexOf("OS");
if ((agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && start > -1) {
return window.Number(agent.substr(start + 3, 3).replace("_", "."))
}
return 0;
}
},
const checkVersion = Common.checkVersion();
require("../assets/style.css");
require("../assets/style2.css");
require("../assets/sprite.css");
require("../assets/ondemandpagestyle.css");
require("../assets/newstyle.css");
require("../assets/landingpage.css");
if (checkVersion <= 14) {
require("../assets/styleIOSUpgrade.css");
}
Here u see I have require the last CSS file conditionally(according to IOS version) , here i have used CheckVersion function to detect the Ios version and according to it and i have require the different CSS file
checkVersion() {
if (typeof window !== 'undefined' && window && window.navigator && window.navigator !== undefined) {
let agent = window.navigator.userAgent, start = agent.indexOf("OS");
if ((agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && start > -1) {
return window.Number(agent.substr(start + 3, 3).replace("_", "."))
}
return 0;
}
},
const checkVersion = checkVersion();
require("../assets/style.css");
require("../assets/style2.css");
require("../assets/sprite.css");
require("../assets/ondemandpagestyle.css");
require("../assets/newstyle.css");
require("../assets/landingpage.css");
if (checkVersion <= 14) {
require("../assets/styleIOSUpgrade.css");
}
U can use above code to require CSS file conditionally

Or operator not working in firebase rules

I have a realtime database. In rules, doing (a || b) works, but doing (b || a) does not work. I couldn't find the reason. Since my rules are long, I'm putting a small part of it here. actually it was working until today but today it stopped working and i didn't change anything.
Not working :
{
"rules": {
"allGames": {
"$gameID": {
".read": true
".write": "auth != null && ((!data.exists() &&
(newData.child('players').child('0').child('userID').val().contains(auth.uid) ||
newData.child('players').child('1').child('userID').val().contains(auth.uid) ||
newData.child('players').child('2').child('userID').val().contains(auth.uid) ||
newData.child('players').child('3').child('userID').val().contains(auth.uid)) ) ||
(!data.exists() &&
(newData.child('0').child('name').val().contains(auth.uid) ||
newData.child('1').child('name').val().contains(auth.uid) ||
newData.child('2').child('name').val().contains(auth.uid) ||
newData.child('3').child('name').val().contains(auth.uid)) ) )"
}
}
}
}
my json:
[
{
"durum": "davetYollayan",
"name": "PzbcSmKziaOcd4PdYNPnIWuG2iH2",
"score": 0
},
{
"durum": "davetYollayan",
"name": "efezefebcSmKziaOcd4PdYNPnIWuG2iH2",
"score": 0
}
]
Screenshot:
it works when i replace the write code with this. but both codes are the same, I don't understand why it gives an error?
".write": "auth != null && ((!data.exists() &&
(newData.child('0').child('name').val().contains(auth.uid) ||
newData.child('1').child('name').val().contains(auth.uid) ||
newData.child('2').child('name').val().contains(auth.uid) ||
newData.child('3').child('name').val().contains(auth.uid)) ) ||
(!data.exists() &&
(newData.child('players').child('0').child('userID').val().contains(auth.uid) ||
newData.child('players').child('1').child('userID').val().contains(auth.uid) ||
newData.child('players').child('2').child('userID').val().contains(auth.uid) ||
newData.child('players').child('3').child('userID').val().contains(auth.uid)) ))"

Show "-" if date is empty in dev extreme datagrid

I'm using dev extreme data grid, I displayed blank if the date is not available.
I want to show "-" if date is empty.
I tried
// component.html
[customizeText]="customizeMyText"
// component.ts
customizeMyText(cellInfo: any) {
console.log(cellInfo);
if (cellInfo.value == '' || cellInfo.value == null || cellInfo.value == undefined) {
return 'NA';
} else {
return cellInfo.value;
}
}
But it gives an error, text.replace is not a function.
The return value on the customizeText function expects a string, change your function to use the valueText instead:
customizeMyText(cellInfo) {
if (
cellInfo.value === "" ||
cellInfo.value === null ||
cellInfo.value === undefined
) {
return "NA";
} else {
return cellInfo.valueText;
}
};
Source: https://js.devexpress.com/Documentation/ApiReference/UI_Widgets/dxDataGrid/Configuration/columns/#customizeText

Vue.js - Update computed property after async computed property gets updated

I have a computed property (filteredSyms) that depends on the asynchronous computed property (allSynonyms). I am using async-computed plugin for this:
https://www.npmjs.com/package/vue-async-computed.
However, when the data gets updated the computed property doesn't wait until the result of the async property update. Therefore, I receive not up to date information. Then after the async property actually return new value computed property doesn't run update again.
How can I make it work the way that computer property waits until there is a result from the async computed property?
The code is below:
asyncComputed: {
async allSynonyms() {
let allSyns = await this.$axios.$post('/db/sym/synonyms', this.model.syms);
return allSyns;
}
},
computed: {
filteredSyms() {
let that = this;
let allSyn = this.allSynonyms;
let exactMatch = this.symsByRating.filter(
function (v) {
let isExactMatch = v.title.toLocaleLowerCase().indexOf(that.searchString.toLocaleLowerCase()) >= 0;
return !that.idsToFilter.includes(v.id) && isExactMatch
&& (!that.currentBodyPart || v.bodyParts.indexOf(that.currentBodyPart) >= 0)
&& that.hasMoreSubsyms(v)
&& (!allSyn || !that.containsObject(v, allSyn))
&& (v.sex == that.model.sex || v.sex == 'NA');
});
let partialList = [];
exactMatch.forEach(ex => partialList.push({n: 100, sym: ex}));
for (let sym of this.symsByRating ) {
let searchWords = this.searchString.toLocaleLowerCase().split(' ');
let symWords = sym.title.toLocaleLowerCase().split(' ');
let n = 0;
let isPartialMatch = false;
symLoop:for (let symWord of symWords) {
symWord = symWord.substring(0, symWord.length - 1);
for (let searchWord of searchWords) {
// don't count last letters of the words
searchWord = searchWord.substring(0, searchWord.length - 1);
if (searchWord.length > 2 && symWord.indexOf(searchWord) >= 0) {
n++;
isPartialMatch = true;
}
}
}
if (exactMatch.indexOf(sym) < 0 && isPartialMatch
&& (!this.currentBodyPart || sym.bodyParts.indexOf(this.currentBodyPart) >= 0)
&& this.hasMoreSubsyms(sym)
&& (!allSyn || !this.containsObject(sym, allSyn))
&& (sym.sex == that.model.sex || sym.sex == 'NA')) {
partialList.push({n: n, sym: sym});
}
}
partialList.sort(function(obj1, obj2) {
return obj2.n - obj1.n;
});
if (this.searchString && this.searchString != '') {
partialList = this.filterSynonyms(partialList);
}
let fs = partialList.map(ws => ws.sym);
console.dir(fs);
return fs;
}
}
A lot of stuff is going on the filtered method, but I guess the main point here that it is using this.allSynonyms to do the check but it is not updated at the time filteredSyms is executed.
Thanks for your suggestions!
(I haven't really tested this out, but it should work.)
vue-async-computed does provide the status in this.$asyncComputed.allSynonyms.success.
try adding this.$asyncComputed.allSynonyms.success as a dependencies to filteredSyms and it should update when success state change.

Firestore Security Rules Failing Because They're Too Long

It appears my security rules are failing because they're too long. The two rules that are commented out cause the whole rule set to fail, but when run together in isolation, they both run successfully. Is there a limit I'm hitting that I'm unaware about?
match /transactions/{transactionId} {
allow create, update: if
isSignedIn() &&
validateTransactionSchema() &&
// Succeeds when these rules are left out.
// These rules succeed on their own, but not when combined with others
// (incomingData().categoryId == null || categoryExists(incomingData().categoryId)) &&
// (incomingData().payeeId == null || payeeExists(incomingData().payeeId)) &&
accountExists(incomingData().accountId) &&
isBudgetOwner() &&
isPremium();
function validateTransactionSchema() {
return incomingData().keys().hasAll(['transactionDate', 'accountId', 'payeeId', 'categoryId', 'splits', 'memo', 'amount', 'cleared', 'locked']) &&
incomingData().size() == 9 &&
incomingData().transactionDate is timestamp &&
incomingData().accountId is string &&
(incomingData().payeeId == null || incomingData().payeeId is string) &&
(incomingData().categoryId == null || incomingData().categoryId is string) &&
incomingData().splits is list &&
(incomingData().memo == null || incomingData().memo is string) &&
incomingData().amount is number &&
incomingData().cleared is bool &&
incomingData().locked is bool;
}
}
function isSignedIn() {
return request.auth != null;
}
function isPremium() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isPremium == true;
}
function isBudgetOwner() {
return get(/databases/$(database)/documents/budgets/$(budgetId)).data.userId == request.auth.uid;
}
function categoryExists(categoryId) {
return exists(/databases/$(database)/documents/budgets/$(budgetId)/categories/$(categoryId));
}
function accountExists(accountId) {
return exists(/databases/$(database)/documents/budgets/$(budgetId)/accounts/$(accountId));
}
function payeeExists(payeeId) {
return exists(/databases/$(database)/documents/budgets/$(budgetId)/payees/$(payeeId));
}
function incomingData() {
return request.resource.data;
}
The limit exposed by Bob Snyder has been raised to 10. This should help your situation.
As per: https://firebase.googleblog.com/2018/06/announcing-firestore-security-rules.html

Resources