nodejs' req.hostname as mongo doc index. bad practice? - node.js

I'm building a nodejs app I would like to make available under various settings by my customers. I want my customers to build their websites on top of my app, so: - the app can be configured in a way that domains "A.com" and "B.com" use the configuration "X" of the app, while "C.com" uses "Y", etc. - I'm thinking about using kubernetes and dockers containers to isolate version "X", "Y" etc. of my app - I'm also thinking about using nginx for reverse-proxy
Each domain will have its distinct documents in a mongodb collection. Because I need a way to retrieve the right data, I'm giving each domain a unique token matching their documents. All the tokens are also saved in mongodb and will be retrieved on my (nodejs) server's start or updated on the fly. The tokens will be saved on a "app.locals" variable: - I'm thinking about having a middleware that will grap the domain from every request (req.hostname) and get the corresponding token
// set locals
app.locals.domainTokens = {};
after retrieving and assigning the tokens, "domainTokens" will look like:
app.locals.domainToken = {
"Acom" : "token_1",
"Bcom" " "token_2" // and so on
}
// the middleware
function grabToken(req, res, next) {
var tokens = app.locals.domainToken,
host = req.hostname;
req.params.token = host ? tokens[host] : undefined;
next();
}
When new customers register, tokens will be assigned on the fly for their domain (containers talk to each other, right?).
So basically, when a request is made for "A.com", my middleware sets the "token" so that the mongodb documents related to "A.com" can be found and sent back to client running version "X" of my app (nginx).
What do you think about this approach ? is it unsafe? a bad practice? do you have a better suggestion to implement it? Am I better off using one container per domain ? (I would have to monitor hundreds of nodejs instances instead of 3 or 4)
Also, is it possible to dynamically configure a Nginx server?

Related

How to create multiple instances of nodejs server

I am building a notification application in which every time an event is triggered the associated user gets a notification. I am using localstorage of NodeJs to store the information of the logged in user. The problem is that when two users are logged in,the localstorage values are overridden by the new users value.
I need to create multiple instances of NodeJS server so that every user has its own localStorage.
For example
If two users log in with credentials
{
userName:name1
}
and
{
userName:name2
}
then two separate localStorage should be created one having userName:name1 and one having userName:name2.
I have tried all the solutions available online but I am unable to create multiple states of NodeJS server.
Thanks in advance
You do not have to create a new server for each user. Take the IP address and port instead. This means that each user can be uniquely identified. You can simply name the files of the users after the variable client.
Here an example code
net.createServer((netSocket : net.Socket) => {
netSocket.on('data', (data) => {
var client = netSocket.remoteAddress + ':' + netSocket.remotePort;
})
})
I wasn't able to create multiple nodejs instances hence I stored the user data in session storage and passed it to nodejs server every time I triggered a request.

unexpected `server_admin required` error with Cloudant NodeJS

I am using the Cloudant service via the Node.JS module, with the credentials provided via Bluemix VCAP_SERVICES (or a local copy). The instance is created with
var Cloudant = require('cloudant');
// var cloudantCreds obtained from process.env.VCAP_SERVICES
var username = cloudantCreds.username;
var password = cloudantCreds.password;
var cloudant = Cloudant({
account:username,
password:password
});
I had written a function that would automatically create/delete a database when a user requested, and it successfully worked. This internally used
cloudant.db.create(databaseName, function(err, res){
// Handle
});
However, recently I get an error:
'server_admin access is required for this request'
I am only using one set of credentials and one account. Using these credentials in the command line with curl allows me to successfully create/delete databases, but it seems to be unable to do this via the node.js module?
As far as I can remember, I haven't changed any code related to this function of my Node.js server.
What is causing me to now require server_admin access? From the nature of the error message, I am presumably authenticated, but not authorised?
It turns out it was an error on my behalf, although Cloudant was not particularly informative.
I was trying to create databases with disallowed names, such as beginning with an underscore or containing a capital letter. Change the database name, and it all works correctly.
if your database is prefixed with underscore it will throw errors...

Exposing Meteor's mongo DB to a stateless client

I need a legacy java application to pull information from a meteor's collection.
Ideally, I would need a simple service where my app would be able to download the latest list of items prices. A scenario like going on (through an http GET):
www.mystore.com/listOfPrices
would return a json with an array
[{"item":"beer", price:"2.50"}, {"item":"water":, price:"1"}]
The problem is that I cannot make a meteor page printing the result "as is" because meteor assumes the client supports javascript. Note that I do plan to implement the java DDP client in a latter stage but here I would like to start with a very simple service.
Idea: I thought of doing my own Node.js request aside of the running meteor service in order to retrieve a snapshot of the collection. Then this request would be using a server based javascript DDP client in order to subscribe and filter to then return the collection once loaded as a jSON document (array).
Any idea on how to achieve this ?
Looks like you want to provide a REST interface. See the MeteorPedia page on REST for how to expose collection data. It might be as simple as
prices = new Mongo.Collection('prices');
// Add access points for `GET`, `POST`, `PUT`, `DELETE`
HTTP.publish({collection: prices}, function (data) {
// here you have access to this.userId, this.query, this.params
return prices.find({});
});

Using NodeJs with Firebase - Security

Due to the need to do some server side code - mainly sending emails I have decided to use Nodejs & Express for the server side element along with Firebase to hold the data - Partly from a learning experience.
My question is whats the best approach with regards to using the client side Firebase library and the Nodejs library when doing authentication using the Simple Email & Password API. If I do the authentication client side and then subsequently call a different route on the NodeJS side will the authentication for that user be carried across in the request. What would be the approach to test the user is authenticated within Node.
One approach I assume is to get the current users username & password from firebase and then post these to NodeJS and then use the firebase security API on the server to test.
Essentially the problem here is you need to securely convey to your NodeJS server who the client is authenticated as to Firebase. There are several ways you could go about this, but the easiest is probably to have all of your client<->NodeJS communication go through Firebase itself.
So instead of having the client hit a REST endpoint served by your NodeJS server, have the client write to a Firebase location that your NodeJS server is monitoring. Then you can use Firebase Security Rules to validate the data written by the client and your server can trust it.
For example, if you wanted to make it so users could send arbitrary emails through your app (with your NodeJS server taking care of actually sending the emails), you could have a /emails_to_send location with rules something like this:
{
"rules": {
"emails_to_send": {
"$id": {
".write": "!data.exists() && newData.child('from').val() == auth.email",
".validate": "newData.hasChildren(['from', 'to', 'subject', 'body'])"
}
}
}
}
Then in the client you can do:
ref.child('emails_to_send').push({
from: 'my_email#foo.com',
to: 'joe#example.com',
subject: 'hi',
body: 'Hey, how\'s it going?'
});
And in your NodeJS code you could call .auth() with your Firebase Secret (so you can read and write everything) and then do:
ref.child('emails_to_send').on('child_added', function(emailSnap) {
var email = emailSnap.val();
sendEmailHelper(email.from, email.to, email.subject, email.body);
// Remove it now that we've processed it.
emailSnap.ref().remove();
});
This is going to be the easiest as well as the most correct solution. For example, if the user logs out via Firebase, they'll no longer be able to write to Firebase so they'll no longer be able to make your NodeJS server send emails, which is most likely the behavior you'd want. It also means if your server is temporarily down, when you start it back up, it'll "catch up" sending emails and everything will continue to work.
The above seems like a roundabout way of doing things, I would use something like https://www.npmjs.com/package/connect-session-firebase and keep firebase as the model, handling all routes through express. Easier if your express server is rendering templates and not just behaving as a JSON API.
If you are using Firebase Authentication, the client side can import the Firebase Library (e.g. for javascript) and authenticate directly with the library itself
import firebase from 'firebase/app';
const result = await firebase.auth().signInWithEmailAndPassword(_email, _password);
After that, the client can to obtain the ID Token, this token will be informed on each request that will be made to the server (e.g. as header).
const sendingIdToken = await firebase.auth().currentUser.getIdToken();
On the Node.js server side, you can install the Firebase Admin SDK, to verify if the user is authenticated on the Node.js server, like:
// Let's suppose the client informed the token as header
const receivingIdToken = req.headers['auth-token'];
admin.auth().verifyIdToken(receivingIdToken, true)
.then((decodedIdToken) => { /* proceed to send emails, etc */}, (error) => {...});
The Firebase Admin SDK gives full permissions to the Database, so keep the credentials safe.
You should also configure the Security Rules on Firestore (or Firebase Realtime), so the client side can still perform specific operations directly to the database (e.g. listening for realtime changes on a collection), but you can also restrict all access if you want the client to only interact with the node.js server.
For more details, I developed an example of a node.js server that uses the Firestore Database and handles security and more.

How do I set up different node.js environments with different mongo databases?

I want to use a local mongo server in development and then switch to a mongolab db in production. What is the recommended way to do this in a node.js express app? All I know is that NODE_ENV exists but not sure on the best way to switch the database based on its value. Having:
if (process.env.NODE_ENV == "production")
{
//
}
else if ((process.env.NODE_ENV == "development")
{
}
All over the place where I query the db doesn't seem very elegant. Is there a way to do it similar to a rail app where you specify you adapter for production and development?
You should only need to connect to your DB using a URL once at application start time, and there you can use code like you have to choose the correct URL based on the runtime environment. Once connected, you app should reference a shared database connection object to perform queries and operations as normal. You may want to handle this with a configuration object as I describe in ExpressJS How to structure an application?

Resources