Get bunch of users from Firebase given Uids - firebase

I wanted to use the function getUsers from auth given a set amount of uids. I can see how to set just one but getting a bunch all together in one call rather than making a bunch doesn't seem to be documented.
Documentation I'm looking at: https://firebase.google.com/docs/auth/admin/manage-users#node.js_3
This example shows
getAuth()
  .getUsers([
    { uid: 'uid1' },
    { email: 'user2#example.com' },
    { phoneNumber: '+15555550003' },
    { providerId: 'google.com', providerUid: 'google_uid4' },
  ])
but what I'm looking for is a way to just supply a bunch of uids and get them all in one call.
Tried different ways of adding uids in this call but don't seem the have the right format. Wondering if there's another function I should be using instead.

Related

Dynamic Google Cloud Firestore rules on document ID

Thanks for taking the time to read!
What I'm trying to do is dynamically compare the requested document ID against another string saved in the custom claims of a user, using Google Firestore.
I've added the code below (removed the unimportant rules for this question) which from reading the docs seems to be correct, but when I try and use these rules it always returns to false. In fact, when I compare orgId to a string of the document like orgId == 'T90101' that also returns false.
What am I missing?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Organization ID saved on the custom claims for this user
function getMyOrgId() {
return request.auth.token.organizationId
}
// BUG: This equates to false but should be true
function verifyOrganizationClaim(orgId) {
return getMyOrgId() == orgId;
}
match /organizations/{orgId} {
allow create: if false;
allow delete: if false;
allow update: if verifyOrganizationClaim(orgId);
allow read: if verifyOrganizationClaim(orgId);
}
}
}
In summary - how do I compare a document ID against a saved custom claim ID, to restrict access to documents?
Any help would be appreciated!
Here's the code I'm using to access this data on the client side using React + TypeScript. The error I get is Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
import React from "react";
import firebase from "firebase";
import { useDocumentDataOnce } from "react-firebase-hooks/firestore";
export const firestore = firebase.firestore();
const MY_ORG_ID = "T01JLKU9W7L"; // This is the same value saved in the custom claims for this user
const EXTERNAL_ORG_ID = "T01JLKU9W7L";
interface Organization {
id: string;
displayName: string;
}
const Example: React.FC = () => {
const [myOrg] = useDocumentDataOnce<Organization>(
firestore.doc(`organizations/${MY_ORG_ID}`)
);
const [externalOrg] = useDocumentDataOnce<Organization>(
firestore.doc(`organizations/${EXTERNAL_ORG_ID}`)
);
return (
<>
{/** Should succeed */}
<div>
<h1>My Organization</h1>
<p>Organization Name: {myOrg?.displayName}</p>
<p>Organization Id: {myOrg?.id}</p>
</div>
{/** Should fail */}
<div>
<h1>Other Organization</h1>
<p>Organization Name: {externalOrg?.displayName}</p>
<p>Organization Id: {externalOrg?.id}</p>
</div>
</>
);
};
export default Example;
I've tried your Security Rules in one of my test environment and I can confirm that the if verifyOrganizationClaim(orgId); statement does work (together with the two functions).
So I can see three possible reasons why it does not work on your side:
Your user is not authenticated
You incorrectly assigned the Custom Claim to the user. You can check the custom claims with the following CLI command: $ firebase auth:export users.csv
You try to create a document, instead of updating or reading it. As a matter of fact your rules only set access rights for updating or reading. (I'm not verse in React and I cannot deduce from the code in your question which operation you execute...)

Firestore security rules with spaces in path

I need to create a firestore rule for a sub collection called "Test Cases". Since firestore rules aren't written in javascript, I can't seem to get the path after match to accept a space without an error.
I've tried quotes, backslashes for escape characters, and putting the whole path in quotes. I haven't found anything for this in the firestore documentation or on stack overflow.
How can I allow a spaces in the path after match, in the example below, in the path including "Test Cases"?
service cloud.firestore {
match /databases/{database}/documents {
match /companies/{company} {
allow read: if getUserCompany() == company || userHasAnyRole(['Super', 'Manager']);
allow write: if getUserCompany() == company || userHasAnyRole(['Super', 'Manager']);
match /Test Cases/{tests} {
allow read, write: if isSignedIn();
}
}
According to fire base support:
To fix this, you can encode the space within security rules using %20. So the rules would be:
Service cloud.firestore { 
match /databases/{database}/documents { 
match /companies/{company} { 
allow read: if getUserCompany() == company || userHasAnyRole(['Super', 'Manager']); 
allow write: if getUserCompany() == company || userHasAnyRole(['Super', 'Manager']); 
match /Test%20Cases/{tests} {                      <------- 
allow read, write: if isSignedIn(); 
} 
} 
} 
I tried it and worked for me. Please give it a try and let us know if you have any issues. 

Firebase Realtime Database currently gives TRIGGER_PAYLOAD_TOO_LARGE error

Since this morning, our Firebase application has a problem when writing data to the Realtime Database instance. Even the simplest task, such as adding one key-value pair to an object triggers
Error: TRIGGER_PAYLOAD_TOO_LARGE: This request would cause a function payload exceeding the maximum size allowed.
It is especially strange since nothing in our code or database has changed for more than 24 hours.
Even something as simple as
Database.ref('environments/' + envkey).child('orders/' + orderkey).ref.set({a:1})
triggers the error.
Apperently, the size of the payload is not the problem, but what could be causing this?
Database structure, as requested
environments
+-env1
+-env2
--+orders
---+223344
-----customer: "Peters"
-----country: "NL"
-----+items
------item1
-------code: "a"
-------value: "b"
------item2
-------code: "x"
-------value: "2"
Ok I figured this out. The issue is not related to your write function, but to one of the cloud functions the write action would trigger.
For example, we have a structure like:
/collections/data/abcd/items/a
in JSON:
"collections": {
"data": {
"abc": {
"name": "example Col",
"itemCount": 5,
"items": {
"a": {"name": "a"},
"b": {"name": "b"},
"c": {"name": "c"},
"d": {"name": "d"},
"e": {"name": "e"},
}
}
}
}
Any write into an item was failing at all whatsoever. API, Javascript, even a basic write in the console.
I decided to look at our cloud functions and found this:
const countItems = (collectionId) => {
return firebaseAdmin.database().ref(`/collections/data/${collectionId}/items`).once('value')
.then(snapshot => {
const items = snapshot.val();
const filtered = Object.keys(items).filter(key => {
const item = items[key];
return (item && !item.trash);
});
return firebaseAdmin.database().ref(`/collections/meta/${collectionId}/itemsCount`)
.set(filtered.length);
});
};
export const onCollectionItemAdd = functions.database.ref('/collections/data/{collectionId}/items/{itemId}')
.onCreate((change, context) => {
const { collectionId } = context.params;
return countItems(collectionId);
});
On it's own it's nothing, but that trigger reads for ALL items and by default firebase cloud functions send's the entire snapshot to the CF even if we don't use it. In Fact it sends the previous and after values too, so if you (like us) have a TON of items at that point my guess it the payload that it tries to send to the cloud function is way too big.
I removed the count functions from our CF and boom, back to normal. Not sure the "correct" way to do the count if we can't have the trigger at all, but I'll update this if we do...
The TRIGGER_PAYLOAD_TOO_LARGE error is part of a new feature Firebase is rolling out, where our existing RTDB limits are being strictly enforced. The reason for the change is to make sure that we aren't silently dropping any Cloud Functions triggers, since any event exceeding those limits can't be sent to Functions.
You can turn this feature off yourself by making this REST call:
curl -X PUT -d "false" https://<namespace>.firebaseio.com/.settings/strictTriggerValidation/.json?auth\=<SECRET>
Where <SECRET> is your DB secret
Note that if you disable this, the requests that are currently failing may go through, but any Cloud Functions you have that trigger on the requests exceeding our limits will fail to run. If you are using database triggers for your functions, I would recommend you re-structure your requests so that they stay within the limits.

No results from Azure Cognitive Services Image Search API when imageType query parameter is set to "Shopping"

I am trying to use the Microsoft Cognitive Services Image Search API to search for images and return ecommerce results.
Until recently, this was working perfectly, but now any searches I perform with the query parameter imageType: 'Shopping' come back empty. If I perform the same search with any other imageType filter, or without the filter at all, I get many results, including many results where insightsSourcesSummary.shoppingSourcesCount is greater than 0.
Here is the code I wrote to call the API:
const
promise = require('bluebird');
rp = require('request-promise');
var Bing = function () {};
Bing.prototype.imageSearch = function(q, count) {
var options = {
method: 'GET',
uri: "https://api.cognitive.microsoft.com/bing/v5.0/images/search",
qs: {
q: q,
count: count,
mkt: 'en-us',
safeSearch: 'Moderate',
imageType: 'Shopping'
},
headers: {
'Ocp-Apim-Subscription-Key': '****************'
},
json: true
};
return rp(options);
}
module.exports = Bing;
Again, I did not change any code between October 2016 when it was working, and now when it is not. Is this a new bug with the API or something I'm doing wrong?
Edited: a curl example for your convenience (you will need to specify a subscription key):
curl -H "Ocp-Apim-Subscription-Key:****************" "https://api.cognitive.microsoft.com/bing/v5.0/images/search?q=shoes&imageType=Shopping"

Storing User Data Flattened Security

I'd like to create an app that has a lot of user data. Let's say that each user tracks their own time per task. If I were to store it flattened it would look like this:
{
users: {
USER_ID_1: {
name: 'Mat',
tasks: {
TASK_ID_1: true,
TASK_ID_2: true,
...
}
},
},
tasks: {
TASK_ID_1: {
start: 0,
end: 1
},
TASK_ID_2: {
start: 1,
end: 2
}
}
}
Now I'd like to query and get all the task information for the user. Right now the data is small. From their guides: https://www.firebase.com/docs/web/guide/structuring-data.html it says (near the end) "... Until we get into tens of thousands of records..." and then doesn't explain how to handle that.
So my question is as follows. I know we can't do filtering via security, but can I use security to limit what people have access to and then when searching base it off the user id? My structure would then turn to this:
{
users: {
USER_ID_1: {
name: 'Mat'
}
},
tasks: {
TASK_ID_1: {
user: USER_ID_1,
start: 0,
end: 1
},
TASK_ID_2: {
user: USER_ID_1,
start: 1,
end: 2
},
...
}
}
Then I would set up my security rules to only allow each task to be accessed by the user who created it, and my ref query would look like this:
var ref = new Firebase("https://MY_FIREBASE.firebaseio.com/");
$scope.tasks = $firebaseArray(ref.child('tasks/')
.orderByChild('user')
.startAt('USER_ID_1')
.endAt('USER_ID_1'));
Is that how I should structure it? My query works but I'm unsure if it'll work once I introduce security where one user can't see another users tasks.
You've already read that security rules can not be used to filter data. Not even creative data modeling can change that. :-)
To properly secure access to your tasks you'll need something like:
"tasks": {
"$taskid": {
".read": "auth.uid === data.child(user).val()"
}
}
With this each user can only read their own tasks.
But with these rules, your query won't work. At it's most core your query is reading from tasks here:
ref.child('tasks/')...some-filtering...on(...
And since your user does not have read permission on tasks this read operation fails.
If you'd give the user read permission on tasks the read and query would work, but the user could then also read all tasks that you don't want to give them access to.

Resources