Firestore: remove sensitive fields on documents - firebase

I'm trying to figure it out how to remove a sensitive field on a firestore document. For example, my collection is a group information. The group is protected with a pin code field. Any one wants to join the group has to know the pin code.
In the meantime, I want to let users query what group is available to join. For query part, I don't want return group information with pin code information. Do we have anyway to remove sensitive fields from a document for Firestore for reading event?
Cloud function only supports write event. 1 possible solution is use cloud function on write event, and put pin code in a separate document. Is there a better solution? THanks.
My group schema is:
group: {
name: string,
pinCode: string
}

A user can either access a document, or they can't. There is no property-level access control in Firestore.
So to accomplish what you want, you will need to store the public and private information in separate documents.
You could either create a second document with the private information in the same collection and then secure them using:
match /databases/{database}/documents {
match /groups/{group} {
allow read: if resource.data.visibility != "private"
}
}
Alternatively (and simpler to secure) you could create a separate collection for the private documents.

You can create a Firebase Function that returns only the fields that you need (non sensitive), here an example:
exports.getTopUsers = functions.https.onCall(async (data) => {
const users = [];
return db.collection('users').orderBy('bids').limit(data.limit).get()
.then((querySnapshot) => {
querySnapshot.forEach((user) => {
users.push({
diplayName: user.get('displayName'),
});
});
return {
topUsers: users,
};
})
.catch((err) => {
console.error(err);
});
});
So, you need to create a separate array (that will be returned) and filling it with only the field that you want while iterating your Firestore collection.

Related

Upsert in Firebase Firestore without the onFailure callback

There must be a better way to make upsert in Firebase Firestore in Kotlin.
I have collection of users that contains another collection userDocuments that contains field called highlights containing list of highlights.
I cannot use set and merge options as that will override the highlights list.
Any ideas how to make the code better. I do not like making two database requests on create and handling the failure like this. Maybe my database structure can be also optimized but I thought it is smart as all private userData will be stored in users collections with some subcollections.
My database structure is like this:
users -> {userId} -> userDocuments -> {docId} -> highlights ["this will be highlighted"]
users, and userDocuments are collections. Highlights is a field on userDocument.
docId might not yet be there, there will be 1000 of documents. And I do not want to add it to every user. I want it to be there, only when they make a change such as add or remove highlight to list of highlights.
usersCollection
.document(userId)
.collection("userDocuments")
.document(docId)
.update("highlights", FieldValue.arrayUnion(text))
.addOnFailureListener { err ->
// TODO should be handled differently
if (err is FirebaseFirestoreException &&
err.code === FirebaseFirestoreException.Code.NOT_FOUND
) {
val highlights = listOf(text)
usersCollection
.document(it)
.collection("userDocuments")
.document(docId)
.set(mapOf("highlights" to highlights), SetOptions.merge())
}
}
You can update using dictionary notation or dot notation too.
https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects
db.collection("userDocuments")
.document(docId)
.update({
"highlights": FieldValue.arrayUnion(text)
});
You can consider using transactions as I mentioned in the comment above. But not sure if that is what you are looking for.

How to create a Firebase Cloud Function for a Document Created on a Collection inside of another Document and Collection

I have a question with CloudFunctions, I need to create a function on Create of a document inside Firestore.
The problem that I have is the way the data is set up:
So I have a Collection called Chat Rooms which gets documents that will vary, and inside each document, you will have some fields and another subcollection, and inside that subcollection already the thing that I need to get on the function.
The problem that I have is that this function should be aware or access with every document created:
Somehting like:
exports.ChatSent = functions.firestore.document('chatrooms/{Variable part}/chats').onCreate((snap, ctx) => { print('do whatever'); return;});
The problem is I don't know how to write that variable part as this function should be executed whenever a new document is written on the Chats collection of each one of the documents inside the Chatroom collection.
Any Ideas?
You should use twice a wildcard when defining the path, as follows:
exports.ChatSent = functions.firestore.document('chatrooms/{chatroomId}/chats/{chatId}').onCreate((snap, ctx) => {
print('do whatever');
return null;
});
If you need to get the wildcards values, do as follows:
exports.ChatSent = functions.firestore.document('chatrooms/{chatroomId}/chats/{chatId}').onCreate((snap, ctx) => {
const chatroomId = ctx.params.chatroomId;
const chatId = ctx.params.chatId;`
print('do whatever');
return null;
});

How to filter field value and send data into the firestore collection?

In the firestore collection named 'doctor' there are different fields including the field 'role'. I want to add the doctors into firestore with a role named doctor. How can I do this? Following is the code that successfully adds data into the database. If you can, tell me the way to add data with a specific field name. Thanks in advance.
service.ts
create_Newdoctor(Record){
return this.firestore.collection('doctors').add(Record);
}
component.ts
CreateRecord(docForm: NgForm){
let Record = {};
Record['fullName']=this.fullName;
Record['email']=this.email;
this.DoctorService.create_Newdoctor(Record).then(res=> {
this.fullName="";
this.email="";
console.log(res);
this.message = "Added";
}).catch(error=>{
console.log(error);
});
}
Notice that Record is a javascript object, and how you create a document in Cloud firestore, is by passing an object with all the filled attributes into the add method like what you did in service.ts, and how you pass in an attribute is via Record[ATTRIBUTE_NAME] = ATTRIBUTE_VALUE
Hence I believe what you need to just to add in the line Record[‘role’] = “doctors” into component.ts

Determine RTDB url in a trigger function

i m bulding a scalable chat app with RTDB and firestore
here is my raw structure of shards
SHARD1
Chats {
chat01: {
Info: {
// some info about this chatroom
},
Messages ...
}, ....
}
SHARD2...
now i have write triggers on all the info nodes of all the shards.
i want get the ID of the shard
How do i know what shard it actually ran on ?
[EDIT]
console.log(admin.app().name); // it prints "[DEFAULT]" in console
Puf and team please help
When a Realtime Database trigger is invoked, the second argument is an EventContext object that contains information about the database and node that was updated. That object contains a resource string, which has what you're looking for. According to the documentation for that string, it's name property will be formatted as:
projects/_/instances/<databaseInstance>/refs/<databasePath>
The databaseInstance string is what you're looking for. So, you can just split the string on "/" and take the 4th element of that array:
export const yourFunction = functions.database
.instance('yourShard')
.ref('yourNode')
.onCreate((snap, context) => {
const parts = context.resource.name.split('/')
const shard = parts[3]
console.log(shard)
})
If all you need is a reference to the location of the change, in order to perform some changes there, you can just use the ref property on the DataSnapshot that was delivered in the first argument, and build a path relative to there.

Firestore/Cloud functions: Finding a document in array of document references that match criteria

Using Firebase Cloud Functions I'd like to search for documents that contain a certain other document in an array of document references. My structure looks as follows;
Users
name
email
cars
ref to cars/car1 for example
ref to cars/car2 for example
Cars
registration
make
model
There are multiple users and multiple cars. I need to search for users that have a certain 'car' in their car array.
I'm trying to write this in a Cloud Function and have the following;
admin.firestore()
.collection('users')
.where('cars', 'array-contains', registration)
.get().then(doc => {
console.log("TESTING: found the user " + doc.data().email)
return
}).catch(error => {
console.error(error);
});
I know this is currently just searching for the registration string in the array. Is there anyway to search for a specific document reference. I'm using Node.js.
Working code to get all the documents that have a document reference in an array;
// Notify the owner of the car
admin.firestore()
.collection('users')
.where('cars', 'array-contains', carRef)
.get().then(snapshot => {
snapshot.forEach(doc => {
console.log("TESTING found the user " + doc.data().email);
const message = {
notification: {
body: 'Your vehicle (' + carReg + ') recieved a report. Tap here to see!',
},
token: doc.data().cloudMessagingToken
};
sendMessage(message);
});
return
}).catch(error => {
console.error("Error finding a user that has the car in their garage");
console.error(error);
});
If you want to query using reference type fields, you will need to provide a DocumentReference type object to the query. If you pass a DocumentReference to a car, the query should work. For example:
const ref = admin.firestore().collection('Cars').doc(id)
where id is the id of the document.
However, you can't search using values of fields inside the referenced document. Firestore queries only work against data in a single collection at a time. With the way you have your data organized right now, it's not possible to make a single query for all users who have references to cars with a specific registration string field. For that query, you would need to also store an array of registration strings for each user that you could query with array-contains.
Yes, this involves duplication of data, and it's called "denormalization". This is very common in nosql type databases to enable the queries you need.

Resources