Firestore Security on Path - firebase

Using Firestore Security rules, I was wondering if there is anyway to directly view a parent document using path? For example, for any path in a subcollection, I would want to do the following:
match /myRootCollection/{myRootDoc=**} {
allow write: if request.path.split('/')[0] === "myRootDoc1":
}
I understand I can also just have the reference of myRootDoc1 in the subcollection's documents as so:
{
myRootCollection: {
{myRootDoc1}: {
mySubCollection: {
{mySubCollectionDoc1}: {
name: "someUser0",
parent: "myRootDoc1",
},
{mySubCollectionDoc2}: {
name: "someUser1",
parent: "myRootDoc1",
},
{mySubCollectionDoc3}: {
name: "someUser2",
parent: "myRootDoc1",
},
}
{myRootDoc2}: {
mySubCollection: {
{mySubCollectionDoc1}: {
name: "someUser3",
parent: "myRootDoc2",
},
{mySubCollectionDoc2}: {
name: "someUser4",
parent: "myRootDoc2",
},
{mySubCollectionDoc3}: {
name: "someUser5",
parent: "myRootDoc2",
},
}
}
}
}
Here, I would be able to read the mySubCollectionDocs if they are under a specific parent. The above works because I have the field directly in each doc, but is it possible to just read a mySubCollectionDoc and get its parent using path from Firestore Security without the specified field?
If I am able to, how would I able to query using node.js (as Firebase protects against any potential leaky queries)?
Thanks in advance.

Try this:
match /myRootCollection/{myRootDoc}/mySubCollection/{id} {
allow write: if myRootDoc == "xxx";
}

Related

Gridsome context variable not passed to page-query

I can't access a primaryTag variable in my GraphQL page-query.
What I want to achieve is on a blog Post page:
display the post content
display the related posts (based on the first tag)
In my gridsome.server.js
api.createPages(async ({ graphql, createPage }) => {
// Use the Pages API here: https://gridsome.org/docs/pages-api
const { data } = await graphql(`{
allPost {
edges {
node {
id
path
tags {
id
}
}
}
}
}`)
data.allPost.edges.forEach(({ node }) => {
createPage({
path: `${node.path}`,
component: './src/templates/Post.vue',
context: {
id: node.id,
path: node.path,
primaryTag: (node.tags[0] && node.tags[0].id) || '',
}
})
})
})
then in my Post.vue
<page-query>
query Post ($path: String!, $primaryTag: String!) {
post: post (path: $path) {
title
path
content
}
related: allPost(
filter: { tags: { contains: [$primaryTag] }, path: { ne: $path } }
) {
edges {
node {
id
title
path
}
}
}
}
</page-query>
Unfortunately I get the following error: `Variable "$primaryTag" of non-null type "String!" must not be null.
Also, as a side note (and that might be the bug issue) I'm using #gridsome/source-filesystem and #gridsome/transformer-remark to create my Post collection.
If you know how to solve this or have a better approach for getting the related posts, comment below.
Libs:
- gridsome version: 0.6.3
- #gridsome/cli version: 0.1.1`

How to run a `$set` and `$pull` within a Meteor method?

I want to update a collection called SMUProfiles, through a method called classroom.delete. I want to pull out the classroom_id from 2 places inside SMUProfiles i.e. one inside classrooms.owner which has an array of codes, and the other inside array classrooms.students.
I have successfully one the $set part, and now trying to add the $pull, but $pull doesn't seem to work.
Can we do the $set and $pull in such way?
/* Method for deleting Classroom */
'classroom.delete'(classroom_id) {
if (!this.userId) {
throw new Meteor.Error('not-authorised');
}
Classrooms.remove(classroom_id)
let classids = Classrooms.find({ owner: this.userId }).fetch().map(function(classrooms){
return classrooms._id })
//console.log(classids);
SMUProfiles.update({
owner: this.userId,
}, {
$set: {
'classrooms.owner': classids
},
$pull: {
'classrooms.students': classroom_id
}
}
)
}
You're trying to $set and $pull on the same field in the same update - the two operations conflict; so no, you can't use these operators in this way.
You could easily split this into two:
SMUProfiles.update(
{ owner: this.userId },
{ $set: { 'classrooms.owner': classids },
);
SMUProfiles.update(
{ owner: this.userId },
{ $pull: { 'classrooms.students': classroom_id },
);
See e.g. this answer

Firebase equalto dynamic nested child

With a structure of
/archive: {
$userId: {
$archiveKey: {
foo: 1
},
...
},
...
}
Where $userId references a user id and $archiveKey is dynamic, created by .push().
Is it possible to query the archive ref and get all archiveObjects where foo = 1 ? Or do I need to fetch down the whole table, manually dig into $userId and extract the archiveObjects I'm looking for?
Firebase queries can nowadays query nested paths, but the paths cannot be dynamic.
So if you know the uid, you can query that user's archives with:
ref.child(authData.uid).orderByChild('foo').equalTo(1).on(...
If you don't know the uid, then you'll have to create a data structure that allows you to do the lookup:
archive_category_to_uids: {
foo: {
1: {
uid1: true,
uid2: true
}
}
}
A more common way is to separate the archives into their own top-level list and have both users and categories refer to that:
users: {
userId1: {
archiveKey1: true,
...
},
...
},
archives: {
archiveKey1: {
foo: 1,
uid: uid1
},
...
},
archiveCategories: {
foo: {
1: {
archiveKey1: true,
archiveKey2: true
}
}
}
Now you can get find the archives with:
ref.child('archiveCategories/foo/1').once('value', function(keys) {
keys.forEach(function(key) {
ref.child('archives').child(key.key()).once('value', function(snapshot) {
console.log(snapshot.val());
});
};
});
This process is called denormalization and is quite common in NoSQL databases. You're modeling the data for how your application needs to consume it. For more on this and other common patterns, I recommend reading this article on NoSQL data modeling.

Difficulty setting up validation rules for Firebase datastructure

I'm working on setting up validaton rules for a Firebase data structure, created using the Bolt compiler.
I'm currently having the Bolt statement below:
path /sharedEvents/{share} is Boolean[] {
read() { isMailOfCurrentUser( share ) }
create() { isOwnerOfEvent( ...) } //NOT YET CORRECT!
delete() { isOwnerOfEvent( prior(...) } //NOT YET CORRECT!
}
With this, I'm trying to achieve that:
Only users having a mail corresponding to the key of 'share' are allowed to read the data (they use this date to retrieve the key of events shared with them.
Only the owner of an event is able to add/remove the key for his event to the list of shared events.
This second point is where I'm running into trouble -I'm not able to create the create/delete rules- since I have no idea how to reference the keys of the boolean values in the validation rule...
Example data in Firebase for the above bolt statement:
sharedEvents
ZW5kc3dhc0BldmVyeW1hMWwuYml6
-BDKBEvy-hssDhKqVF5w: true
-FDKBEvy-hsDsgsdsf5w: true
-ADBEvy-hfsdsdKqVF5w: true
aXQnc251bWJlcnNAbWExbDJ1LnVz
-KBEvy-hsDhH6OKqVF5w: true
To clarify the needs on this example:
Only user with mail 'ZW5kc3dhc0BldmVyeW1hMWwuYml6' is able to read the three nested childs.
Only the owner of event '-BDKBEvy-hssDhKqVF5w' should be able to create/delete this value. (the same for the other event key/boolean pairs).
My question: is this setup going to work (and how to setup the create/delete rules)? Or is this not going to work and should I rethink/structure the data?
Any help is appreciated!
-----------------OUTPUT JSON FILE------------------------------------------
The question above has been answered, this section is showing the resulting json
"sharedEvents": {
"$share": {
".read": "<removed for readability>",
"$event": {
".validate": "newData.isBoolean()",
".write": "<removed for readability>"
}
}
},
Thanks again for your quick support!
You'll need a nested path statement to handle the restriction on the events (the nodes under /sharedEvents/$mail/$eventid). I quickly prototyped with this JSON structure:
{
"events": {
"-ADBEvy-hfsdsdKqVF5w": {
"name": "Event 1",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-BDKBEvy-hssDhKqVF5w": {
"name": "Event 2",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-FDKBEvy-hsDsgsdsf5w": {
"name": "Event 3",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-KBEvy-hsDhH6OKqVF5w": {
"name": "Event 3",
"ownerMail": "ZW5kc3dhc0BldmVyeW1hMWwuYml6"
}
},
"sharedEvents": {
"ZW5kc3dhc0BldmVyeW1hMWwuYml6": {
"-ADBEvy-hfsdsdKqVF5w": true,
"-BDKBEvy-hssDhKqVF5w": true,
"-FDKBEvy-hsDsgsdsf5w": true
},
"aXQnc251bWJlcnNAbWExbDJ1LnVz": {
"-KBEvy-hsDhH6OKqVF5w": true
}
},
"userMails": {
"peter": "aXQnc251bWJlcnNAbWExbDJ1LnVz",
"puf": "ZW5kc3dhc0BldmVyeW1hMWwuYml6"
}
}
And came up with these rules:
path /sharedEvents/{share} {
read() { isMailOfCurrentUser(share) }
}
path /sharedEvents/{share}/{event} is Boolean {
create() { isOwnerOfEvent(event) }
delete() { isOwnerOfEvent(prior(event)) }
}
isMailOfCurrentUser(share) { true }
getMailOfCurrentUser(uid) { root.ownerMails.uid }
getEventOwnerMail(event) { root.events.event.ownerMail }
isOwnerOfEvent(event) { getMailOfCurrentUser(auth.uid) == getEventOwnerMail(event) }
Ignoring any mistakes on my end, this should be the basics of the authorization structure you're looking for.

Firebase - Index rule for attributes under key created by ChildAutoById

I store my data on Firebase with the format
{
"list": [
"id created by Firebase": {
"foo": "bar"
},
"another id created by Firebase": {
"foo": "bar"
},
...
]
}
I would like to create an index on foo, using Firebase Rules.
However, according to Firebase doc, I need to know the specific ID created by Firebase to put in the rules specification.
Does anyone know of a way to get around this?
You don't need to know the specific ID, that wouldn't be possible. You simply need to have an .indexOn rule on the parent node. https://www.firebase.com/docs/security/guide/indexing-data.html
{
"rules": {
"list": {
".indexOn": ["foo"]
}
}
}
If you need to add additional rules for the children, then you add those normally like so:
{
"rules": {
"list": {
".indexOn": ["foo"],
"$item": {
"foo": {
".validate": "newData.isString()"
}
}
}
}
}

Resources