Firestore security rules: not in condition - google-cloud-firestore

I have game interface with players limit and connected players (array of player ids)
interface IGameData { playersLimit: number; players: string[]; }
How to write a not in rule which will check if player that I'm requesting to push inside players array is already in the players array then the rule should deny request?
Update request looks like this
await updateDoc(
doc(db, 'games', gameId),
{
players: arrayUnion(playerId)
}
)

The arrayUnion operator already prevents duplicates from appearing in the players array. You don't need a security rule for that.

Related

Complex firestore rules

I have a top level collection: "organizations", in that collections doc's there is an employees map like this:
employees: {
uid1: {
displayName: John Do
[...]
}
uid2 {
[...]
}
}
I have an other top collection: "customers" with an organization map like this:
organizations: {
organizationId1: some string,
organizationId2: some other string,
[...]
}
where:
uid is the user id from firebase auth
organizationId is the document id of an organization doc.
user can be in multiple organizations, and customers can be share between multiple organizations as well.
I want to restain acces to customer doc, at user who are employee of at least one organization listed in the customer doc.
Has there is no way to loop in firestore.rules
I think the answer may be mapDiff, and custom claims.
user custom claims:
organizations:[organizationId1, organizationId2, ...]
But i have some difficulty to understand the documentation:
https://firebase.google.com/docs/reference/rules/rules.MapDiff
Is there a way to achive that ?
Maybe I didn't understand it correctly, but you can try something like this:
allow read, write: if employeeOrganization in [organization1, organization2...]
https://firebase.google.com/docs/reference/rules/rules.List
I finaly find the ways to set rules for that:
match /customer/{cId} {
allow create: if request.auth != null;
allow read, update: if (request.auth.token.organisations.keys().hasAny(resource.data.organizationIds.keys()));
allow delete: if false;
}
My custom claims are like:
(there is only 1 to 5 organisations so it's not heavy to transmit)
organizations:{
organizationId1: "role",
organizationId2: "admin",
...
}
My file customer docs as a map like this:
organizationIds{
organization1Id: "some AES key used for security purpose",
organization358Id: "some AES key used for security purpose"
}
It work nice and using custom claims save countless read per day.

elemMatch for fields in different levels

I'm asking this question after trying solve this problem all day long.
I want to get my users address by userId and by addressId. Since I receive data from post requests, I need to ensure that the query contains both userId and addressId in order to avoid security problems. The result of the query below returns all addresses from my user, instead of just the address containing the correct addressId.
async getUserAddress(_id: string, addressId: string) {
const user = await this.userModel.findOne(
{
_id,
addresses: {
$elemMatch: {
addressId: Types.ObjectId(addressId)
}
}
}
)
if (!user) {
throw new NotFoundException();
}
return user.addresses[0];
}
Since is impossible to use $elemMatch in different document levels, or even at the top level of the query, I find no better way to make this query without using filters. Are there any insights?
Tks in advance

Remove a specific nested object from an array of objects

I need to remove a specific object that is nested inside an array of objects.
The following db structure looks like:
I would like to remove one of the teams based on the roomId (to find the specific room) and based on the team approved state. If a team has "approved: false" it needs to be deleted from the array of objects.
I'm using mongoose and came up with the following, but without succes:
Room.update({"roomId": req.params.roomId},
{"$pull": { "teams.approved": false } })
screenshot thats shows the correct roomId:
The array name and equality condition should be specified separately like in this example, try:
await Room.update({"roomId": req.params.roomId}, {"$pull": { "teams": { approved: false } } })
Room.updateOne({ roomId: req.params.roomId}, {"$pull": { "teams": { approved: false } } })
This might work for your case.
I think something like this should work:
Room.find({roomId: 1234}).then((result) => {
if (result) {
return Room.remove({result.teams.approved: false})
}
} ).catch()
You still need to first find all results matching the roomId number and then removing the teams based on approved. There are probably better ways to handle this but I think it's a fair approach.

Send more than one term to algolia search

I'm implementing algolia search in my site and i want to get a set of data matching any id's i send to the search, so i need to know how could i send more than one parameter to the search, so i can send a set of ids, something like this:
let client = algoliasearch(APP_ID, API_KEY),
index = client.initIndex(INDEX_NAME);
let term=["3223212","2423434"];
index.search(term, callback)
This is not working right now, have any idea? or even how could i achieve my goal using another algolia feautre like filtering for instance?
If you're trying to retrieve objects by their objectIDs (which you can manually set at creation time to match your database ids), you can simply use the getObjects method.
Extract from the documentation:
You can also retrieve a set of objects:
index.getObjects(['myObj1', 'myObj2'], function(err, content) {
console.log(content);
});
If you're trying to list all the records that belong to a group with a specific id, you can use a facet that will contain this id and filter on it.
Inside your record:
{
"group_id": "3223212",
// or
"group_ids": ["3223212", "2423434"]
}
Inside your index settings:
{
attributesForFaceting: [
'onlyFilter(group_id)'
]
}
At query time:
let ids = ["3223212", "2423434"];
let filters = ids.map(id => `group_id:${id}`).join(' OR ');
index.search('', { filters: filters }, callback);

Can SC.get return tracks from two specific users?

I'd like to specify two user id's in SoundCloud's SC.get('/tracks') function. Is this possible?
something like this:
function getTracks() {
SC.get('/tracks', {
user_ids: {user_id: 00001, user_id: 000002}
}, function(tracks) {
// build a list of tracks from the two soundcloud users
});
}
It is not possible to filter the /tracks endpoint on user. Sorry!
The closest thing you can do is grab the tracks for each user individually:
SC.get('/users/<user_id>/tracks', function(tracks) {
...
});
So you could do two calls to SC.get() and combine the results.