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
Related
I have this part of code that work but strangely the latest step of outputting the result doesn't work.. When i try to log the first element of array it returns undefined bacause the execution is asynchronous. I thought to build a series of nested callbacks but I think that is a bad practice. Is there any other way to makes it work without create nested promise callbacks?
CODE:
var ImgGalleyURL = [];
//CONTROLLO SE SONO STATE INSERITE IMMAGINI DA CARICARE E LE CARICO
if (postwp.postImgGallery1 != null && postwp.postImgGallery1 != "") {
msg.createMedia(postwp.postImgGallery1).then((imgURL)=>ImgGalleyURL.push(imgURL));
}
if (postwp.postImgGallery2 != null && postwp.postImgGallery2 != "") {
msg.createMedia(postwp.postImgGallery2).then((imgURL)=>ImgGalleyURL.push(imgURL));
}
if (postwp.postImgGallery3 != null && postwp.postImgGallery3 != "") {
msg.createMedia(postwp.postImgGallery3).then((imgURL)=>ImgGalleyURL.push(imgURL));
}
if (postwp.postImgGallery4 != null && postwp.postImgGallery4 != "") {
msg.createMedia(postwp.postImgGallery4).then((imgURL)=>ImgGalleyURL.push(imgURL));
}
if (postwp.postImgGallery5 != null && postwp.postImgGallery5 != "") {
msg.createMedia(postwp.postImgGallery5).then((imgURL)=>ImgGalleyURL.push(imgURL));
}
console.log(ImgGalleyURL[0] + "this is the first image loaded");
Thank you all
I think you're looking for Promise.race:
const promises = [];
for (let i=1; i<=5; i++) {
const propName = "postImgGallery" + i;
if (postwp[propName] != null && postwp[propName] != "") {
promises.push(msg.createMedia(postwp[propName]));
}
}
Promise.race(promises).then(firstUrl => {
console.log(firstUrl + "this is the first image loaded");
});
Promise.all(promises).then(imgGalleryURLs => {
console.log("All images ("+ imgGalleryURLs.join(", ") + ") loaded");
});
You were trying to log the first value of the array when none of the promises was fulfilled yet, so it was still empty.
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
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.
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
This is continuing from an old thread here https://groups.google.com/forum/#!topic/firepad-io/73dKYaUwTn4)
The aim is to clean the database for documents which have many revisions over a long period
I need help writing a function that issue a FB command to delete all revisions which are 'nd' days older than the 'ns' snapshot.
I am not sure of both the Firebase syntax for this command and how to access the relevant firebase keys properly.
Any help will be greatly appreciated
Thx!
ended up solving this
PR: https://github.com/firebase/firepad/pull/264
code:
FirebaseAdapter.prototype.deleteOldRevisions_ = function(query) {
var self=this;
query.once('value', function(s) {
if (typeof s.val() === 'undefined' || s.val() === null || !s.hasChildren()) return;
s.forEach(function(rev) {
utils.log('removing old revision: '+rev.key);
rev.ref.remove();
});
setTimeout(function() { self.deleteOldRevisions_(query); }, 100); // delete the next one
});
}
FirebaseAdapter.prototype.monitorHistory_ = function() {
var self = this;
// Get the latest checkpoint as a starting point so we don't have to re-play entire history.
self.ref_.child('checkpoint').once('value', function(s) {
//utils.log(new Date().toISOString() + ': got checkpoint');
if (self.zombie_) { return; } // just in case we were cleaned up before we got the checkpoint data.
var revisionId = s.child('id').val(), op = s.child('o').val(), author = s.child('a').val();
if (op !== null && revisionId !== null && author !== null &&
op !== undefined && revisionId !== undefined && author !== undefined) {
self.pendingReceivedRevisions_[revisionId] = { o: op, a: author };
self.checkpointRevision_ = revisionFromId(revisionId);
self.monitorHistoryStartingAt_(self.checkpointRevision_ + 1);
} else {
self.checkpointRevision_ = 0;
self.monitorHistoryStartingAt_(self.checkpointRevision_);
}
// delete revisions older than one week before last checkpoint
if (revisionId) {
var historyRef=self.ref_.child('history');
historyRef.child(revisionId+'/t').once('value', function(s) {
if (typeof s.val() !== 'undefined' && s.val() !== null) {
var weekBefore=s.val()-(24*60*60*1000*7);
//utils.log('checkpoint revision: '+self.checkpointRevision_);
//utils.log('checkpoint time: ' + new Date(s.val()));
//utils.log('remove before: ' + new Date(weekBefore));
self.deleteOldRevisions_(historyRef.orderByChild('t').endAt(weekBefore));
}
});
}
});
};