How to get user by email? - node.js

I am using graph-service to access Microsoft Graph API. Following this document to get user by email. I have write a block of code:
var user = null;
const GraphService = require('graph-service');
const ClientCredentials = require('client-credentials');
const tenant = 'my-company.com';
const clientId = '0b13aa29-ca6b-42e8-a083-89e5bccdf141';
const clientSecret = 'lsl2isRe99Flsj32elwe89234ljhasd8239jsad2sl=';
const credentials = new ClientCredentials(tenant, clientId, clientSecret);
const service = new GraphService(credentials);
service.all('/users').then(response => {
console.log(response.data);
response.data.forEach(function(item) {
if (item.userPrincipalName == "lgomez#my-company.com"){
user = item;
break;
}
});
});
But response.data returns a list of users with cost much resource to get a single user object by email. Please help me to update the code which can return only a single object of user by email.

The operation /users you are using, is for retreiving a list of all users.
You can request as single user by calling:
/users/{id | userPrincipalName}
This operation either accepts an userId or an userPrincipleName
e.g.
/users/foo#bar.com
[more information is in the documentation]
In your case the code could be altered to:
//...
var myUPN = "lgomez#my-company.com"
service.get('/users/' + myUPN).then(response => {
console.log(response.data);
});

Related

I have the create and saving of the user's email, but I can't do an email update. Use firebase, firestore and react

This is my code:
exports.saveUserEmail = functions.region('europe-central2').auth.user().onCreate((user) => {
const email = user.email;
const uid = user.uid;
const dt = dateTime.create();
const formatted = dt.format("Y-m-d H:M:S");
return admin.firestore().collection('users').doc(uid).set({uid: uid, email: email, created_at: formatted});
});
and i tried do update like this:
exports.saveEditedEmail = functions.region('europe-central2').auth.user().updateUser((user, uid) => {
const email = user.email;
return admin.firestore().collection('users').doc(uid).set({uid: uid, email: email,});
})
Where is my mistake?
There isn't any onUpdate() auth trigger for Cloud functions. Instead your can create a callable function and call it directly from client side to update your user.
exports.addMessage = functions.https.onCall((data, context) => {
const { email } = data;
// update email in Firebase Auth and Firestore
});
Alternatively, you can directly update the document in Firestore if the user tries to update their own profile. You can setup the following security rules so a user can update their own profile only:
match /users/{userId} {
allow update: if request.auth.uid == userId;
}

Firebase Functions extracting Token

Trying to grab my FCM token from my Cloud Firestore using Firebase Function
my function code:
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotificationToFCMToken = functions.firestore.document('Posts/{likes}').onWrite(async (event) => {
const title = event.after.get('title');
const content = event.after.get('likes');
let userDoc = await admin.firestore().doc('Users').get();
let fcmToken = userDoc.get('{token}');
var message = {
notification: {
title: title,
body: "you have a new like",
},
token: fcmToken,
}
let response = await admin.messaging().send(message);
console.log(response);
});
My Firestore
Posts:
Users:
if I manually add the token everything works but just send every "like" to one device, my goal is to send a link to only the owner of the post
It's probably more alike this:
let userRef = event.after.get('ref'); // obviously the path is mandatory ...
let userDoc = await admin.firestore().doc(userRef).get(); // then this should match
let token = userDoc.get('token'); // and the token should be accessible
Add logging to see what you get: functions.logger.info('🞬🞬🞬 ' + JSON.stringify(event)); ...viewable at https://console.cloud.google.com/logs/query. When listening for Posts/{likes} you'd likely need an additional query - and when listening for Posts, you'd need to determine changes. Getting access to ref is required to make the subsequent query work.
Martin's answer is correct, but it seems that the ref field is of type Reference, see the slash at the beginning, plus the error you get.
So, if this assumption is correct, you should use the path property, as follows (adapting Martin's code):
let userRef = event.after.get('ref'); // obviously the path is mandatory ...
let userDoc = await admin.firestore().doc(userRef.path).get(); // then this should match
let token = userDoc.get('token'); // and the token should be accessible
In addition, in order to correctly manage the life cycle of your Cloud Function, you should do, at the end:
let response = await admin.messaging().send(message);
console.log(response);
return null;
or simply
return admin.messaging().send(message);

How do I make sure my getUserProfile cloud function in firebase only returns data if the logged in user's session requests it?

I have a cloud function called getUserProfile that looks like this:
exports.getUserProfile = functions.https.onRequest((request, res) => {
try {
const username = request.body.username || request.query.username;
if (username == null || username.length == 0) return res.send('{"status":"error","errorMessage":"No username Provided."}');
admin.database().ref().child("users").orderByChild("username").equalTo(username).once('value', snapshot => {
if (!snapshot.exists()) return res.send(`{"status":"error","message":"invalid-profile"}`);
snapshot.forEach(function(child) {
const id = child.val().id;
admin.database().ref('users/' + id).once('value', snapshot => {
if (!snapshot.exists()) return res.send('{"status":"error","errorMessage":"user not found."}');
let userData = {}
userData.username = snapshot.val().username || "N/A";
userData.name = snapshot.val().name || "N/A";
userData.email = snapshot.val().email || "N/A";
admin.database().ref('uploads').orderByChild('createdBy').equalTo(id).once('value', snapshot => {
userData.content = {};
const filePromise = [];
snapshot.forEach(function(child) {
let content = {};
content.id = child.key;
content.val = child.val();
content.val.files.forEach(function(fileId) {
filePromise.push(admin.database().ref().child("files").child(fileId.fileId).once("value"));
});
userData.content[child.key] = content;
userData.content[child.key].files = [];
});
Promise.all(filePromise).then(results => {
results.forEach((result => {
const uploadId = result.val().uploadId;
const thumbDownloadURL = result.val().thumbDownloadURL;
userData.content[uploadId].files.push(thumbDownloadURL);
}));
res.send(`{"status":"ok","userdata":${JSON.stringify(userData)}}`);
});
});
});
});
});
} catch (err) {
res.send(`{"status":"error","message":"${err}"}`);
}
});
I use this function to get the user profile's details and render them on the view. However, if someone makes a request let's say from https://reqbin.com/ along with sending {"username": "xxx"} JSON content, the function is still returning the data which is a security breach, and this is an expected behavior. How do I make sure that only the logged-in user gets the data?
You should use JWT authentication to verify users who want to request data from your firebase cloud function. So when a user first signs up a Token is assigned to that user (which can be renewed or revoked when needed), then whenever that user wants to request data they need to send their token by adding Authentication: Bearer + UserJWToken to the request header. Then on the cloud function's side you can verify that token and
here are some links that could help with the subject:
https://firebase.google.com/docs/auth/admin/create-custom-tokens
https://firebase.google.com/docs/auth/admin/verify-id-tokens
https://itnext.io/firebase-cloud-functions-verify-users-tokens-d4e60e314d1a

What is the proper way to update a particular column in nestjs

I want to save token generated into the user's confirmed email column. The token is part of the confirmation link that will be sent to the user so that when the user clicks on the link I can check if it matches, then updates it to "activated".
Now the problem is I can't figure out how to save it in the ConfirmEmailLink method .
async register(createDTO: CreateUserDto) {
const { email } = createDTO;
const user = await this.userModel.findOne({ email })
if (user) {
throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
}
const createdUser = new this.userModel(createDTO);
var newUser = await createdUser.save();
await SendEmail(createDTO.email, await **this.ConfirmEmailLink(createdUser._id)**, createDTO.email);
return this.sanitizeUser(createdUser);
//return null;
}
In the above code there is ConfirmEmailLink that is a parameter to SendEmail method
async ConfirmEmailLink(userId: string) {
const id = v4();
var payload = { userId: userId };
var secret = process.env.JWT_SIMPLE_TOKEN;
var token = jwt.encode(payload, secret);
console.log("This is uuid", userId);
var link = `${process.env.HOST}/user/confirm/${token}/${id}`;
let user = await this.userModel.findById(userId);
if (!user) {
throw new HttpException("Registration not complete, try again or contact admin", HttpStatus.NOT_FOUND);
}
**//This is where the problem is, I want to save the token in ConfirmEmail column**
await this.userModel.updateOne({confirmEmail: token});
return link;
}
I will appreciate your suggestions or if there is a better way to do this
Thanks
updateOne needs 2 parameters, a filter to identify which document to modify, and a update indicating what to do.
.updateOnde({"_id":userId},{"$set":{"confirmEmail": token}})

Access azure billing API

I would like to create a dashboard with graphs about costs of my azure resources (as detailed as possible). Meaning, a list of monthly invoices is not enough (but I would already be very happy if I a could achieve that!!)
Anyway, the first thing I noticed is that if you find an example the endpoint urls look like this
https://management.azure.com/subscriptions/${subscriptionId}/resourcegroups?api-version=2016-09-01
Check the end of the url 2016-09-01, doesn't look very up2date. This medium post was the best article I could find, but it also uses these urls.
Furthermore, I was not able to follow the steps described, first it uses postman to retrieve an access_token (not very useful for me because I need it automated) and second, somewhere in the middle an access_token is retrieved but never used.
So, I found a npm packages like [azure-arm-billing][2] from which I was able to write the following program (mostly copy-paste):
const msRestAzure = require('ms-rest-azure');
const BillingManagement = require('azure-arm-billing')
const clientId = process.env['CLIENT_ID'];
const secret = process.env['APPLICATION_SECRET'];
const domain = process.env['DOMAIN'];
const subscriptionId = process.env['AZURE_SUBSCRIPTION_ID'];
// Retrieve access_token
const app = new msRestAzure.ApplicationTokenCredentials(clientId, domain, secret);
app.getToken((err, token) => {
console.log(token.accessToken);
});
// =======
msRestAzure
.interactiveLogin( { domain }) // The argument here is nowhere documented
.then(credentials => {
console.log(credentials);
let client = new BillingManagement(credentials, subscriptionId);
return client.invoices.list();
})
.then(invoices => {
console.log('List of invoices:');
console.dir(invoices, { depth: null, colors: true });
});
Running this shows a nice access_token and invoices
...
List of invoices:
[
{
id: '/subscriptions/../providers/Microsoft.Billing/invoices/....',
name: '...',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-08-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-09-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/.../pr..s/Micro..ing/bill..ods/201910-1'
]
},
{
id: '/subscriptions/9ea...3d/providers/Microsoft.Billing/invoices/201909-...',
name: '....',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-07-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-08-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/..../providers/Microsoft.Billing/billingPeriods/201909-1...'
]
}
]
Although I have my invoices, there are no numbers. And I would like to retrieve costs for every resources.
So the documentation seems to be outdated up to not existing for what I want (as it seems). My question is if someone was able to retrieve information like this? I would really like to know how!!
UPDATE
It seems to be a permission issue. So, below I share some screenshots showing what I have right now. Maybe from these it is clear what I miss or have setup incorrectly. So first, here is my latest nodejs app:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
const clientId = '76d79....'; // App registration ID
const secret = '****...'; // App registration secret
const domain = 'dc36...'; // tenantId
const subscriptionId = '9ea2d...'; // subscription ID
const AzureServiceClient = msRestAzure.AzureServiceClient;
//an example to list resource groups in a subscription
msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain).then((creds) => {
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = '';
const filter = '';
const skiptoken = '';
const top = 1000;
const apply = '';
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then(result => {
console.log('The result is:', result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, { depth: null, colors: true });
});
Which outputs a statusCode 401
Error: Unauthorized. Request ID: e6b127...
...
So, I have in AD an App registration
Its API permissions are
Finally, I have just one subscription
With the following IAM settings
Any suspicious?
If you're looking for resource costs, I would suggest that you take a look at Consumption API - List Usage Details. That will give you the consumption for all the resources.
You will need to install azure-arm-consumption package.
Here's the sample code:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
msRestAzure.interactiveLogin().then((creds) => {
const subscriptionId = "<your subscription id>";
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = "";
const filter = "";
const skiptoken = "";
const top = 1000;
const apply = "";
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then((result) => {
console.log("The result is:");
console.log(result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, {depth: null, colors: true});
});
This is taken from here: https://github.com/Azure/azure-sdk-for-node/tree/master/lib/services/consumptionManagement.

Resources