No response after database-access in jugglingdb - node.js

I try to use my compound.js-application as a (transparent) proxy-server. When a user tries to request a external website, the application will check, if the user with that ip-address was authenticated before.
If so, the external site will be shown, if not, the user will be encouraged to login.
The problem is, that the response is not processed, when there is an access to the database-object "User".
When I comment out the database section and just use the code inside the anonymous function, the programm works as expected.
action('exturl', function () {
User.all({ where: { ipaddress: req.ip }}, function(err, users) {
if (users.length > 0) {
this.user = user[0];
var proxy = http.createClient(80, req.headers['host'])
var proxy_request = proxy.request(req.method, req.url, req.headers);
proxy_request.addListener('response', function (proxy_response) {
proxy_response.addListener('data', function(chunk) {
res.write(chunk, 'binary');
});
proxy_response.addListener('end', function() {
res.end();
});
res.writeHead(proxy_response.statusCode, proxy_response.headers);
});
req.addListener('data', function(chunk) {
proxy_request.write(chunk, 'binary');
});
req.addListener('end', function() {
proxy_request.end();
});
} else {
redirect(path_to.login);
}
});
});
Is there a failure inside my code? I don't know what I am doing wrong.

Related

Port redirection in node.js

I have a two server(running on two different port), one is for chat application, and another is for API generation server(user should register by providing company
details,and my algorithm gives a API key to the user).
The problem is, i am checking the valid API key,provided by the user, if API key is true then it should redirect to chat server(port no 5200).
But it doesn't work, please give any idea to resolve this issues.
Here is my code,
`
app.post('/checkAPIkey',function(req,res){
var apikey=req.query.apikey;
var apikey1=uuidAPIkey.isAPIKey(apikey);
if(apikey1){
res.writeHead(302, {
Location: 'http://localhost:5200'
});
}else{
res.end("error");
}
});`
What you need is called Request Forwarding.
Example:
const http = require('http');
app.post('/checkAPIkey', function(req,res){
var apikey=req.query.apikey;
var apikey1 = uuidAPIkey.isAPIKey(apikey);
if(apikey1){
const options = {
port: NEW_PORT,
hostname: 'NEW_HOST',
method: 'POST',
path: '/'
};
var reqForward = http.request(options, (newResponse) => {
//Do something with your newResponse
var responseData = "";
newResponse.on('data', (chunk) => {
//Add data response from newResponse
responseData += chunk;
});
newResponse.on('end', () => {
//Nothing more, send it with your original Response
response.send(responseData);
});
});
// If ERROR
reqForward.on('error', (e) => {
console.error('Error: ' + e);
});
// Write to the request
reqForward.write(YOUR_POST_DATA);
reqForward.end();
} else {
res.end("error");
}
});

SocketCluster Middleware HandShake with promise

Im building an app that serve both http and ws. Users login first over HTTP to a Laravel Server. That returns a JWT that is used to allow login over WS.
Ihv added a MIDDLEWARE_HANDSHAKE that gets the token and make a request to Laravel Server to ask if that token is valid and the user has access to WS (Not every logged user is allowed to WS);
Client code:
var options = {
host: '127.0.0.1:3000',
query: {
source: 'web',
token: '',
}
};
var socket;
$.post('http://127.0.0.1:8000/authenticate', {
email: 'chadd01#example.org',
password: '1234'
}, function(data, textStatus, xhr) {
options.query.token = data.token;
//ALL PERFECT UNTILL HERE
// Initiate the connection to the ws server
socket = socketCluster.connect(options)
.on('connect', function(data) {
console.log('CONNECTED', data);
})
.on('error', function(data) {
console.log('ERROR', data.message);
});
});
SocketCluster Server code:
scServer.addMiddleware(scServer.MIDDLEWARE_HANDSHAKE, function(request, next) {
var query = url.parse(request.url, true).query;
switch (query.source) {
case 'web':
case 'mobile-app':
validateUser(query)
.then((response) => {
next(); //Allowed
})
.catch((code) => {
next(code); //Blocked with StatusCode
});
break;
default:
next(true, 'NOT_AUTHORIZED'); // Block
break;
}
});
validateUser = (credentials = {}) => {
return new Promise((resolve, reject) => {
request({ url: API + 'webSocket/users/' + credentials.token, method: 'GET' }, (error, response, body) => {
if (response.statusCode === 200) {
resolve(body);
}
reject(response.statusCode);
});
});
};
While implementing this middleware like this i keep getting this response from ws server even when validation is successfull:
WebSocket connection to 'ws://127.0.0.1:3000/socketcluster/?source=web&token=<_TOKEN_>' failed: Connection closed before receiving a handshake response
(index):149 ERROR Socket hung up
But, if i implement the HANDSHAKE_MIDDLEWARE like this:
scServer.addMiddleware(scServer.MIDDLEWARE_HANDSHAKE, function(request, next) {
var validUser = true;
if (validUser){
return next();
}
return next('NOT_A_VALID_USER');
});
All goes fine:
CONNECTED Object {id: "W067vqBc9Ii8MuIqAAAC", pingTimeout: 20000, isAuthenticated: true, authToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6I…xOTV9.E4bLPh4Vjk9ULvfhW6prjBbVt0vOD32k63L1vlDtGrU"}
So the problem seems to be in the Promise callback.
Any advice if this is not the right way to implement?
Thanks.
A big reason why JWT is used on SocketCluster is to handle logging in and authentication, have you considered just using WS?
Take a look at SocketCluster authentication.
Just how your current HTTP code check the login data, you can do the same for WS and use socket.setAuthToken to set the token (here is an example that I used in my project):
socket.setAuthToken({
email: credentials.email,
id: returnedData.id,
permission: returnedData.permission
});
You can then do requests to the WS server still using on/emit, and do a check to see if they are authenticated. Here's a modified snippet of my authCheck function:
const token = socket.getAuthToken();
if (token && token.email) {
console.log('Token Valid, User is: ', token.email);
// user validated - continue with your code
} else {
console.log('Token Invalid, User not authenticated.');
// respond with error message to the user
}

SailsJS - Nodejs Https-Request. Can't set headers after they are sent

I'm new to Sails.js and I was trying to make a filter to authorize using a Bearer token which come from a higher server, a gatekeeper which is responsable to do the OAuth2 authentication from GitHub API. The services streams works well. I'm already aware of Passport.js but I'm trying to implement this on my own. I came with a policy which looks like:
module.exports = function (req, res, next) {
var httpsExec = require('https');
if (req.headers.authorization) {
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var tokenType = parts[0]
, credentials = parts[1];
if (/^Bearer$/i.test(tokenType) || /^bearer$/i.test(tokenType)) {
httpsExec.request({
host: 'api.github.com',
post: 443,
path: '/user',
method: 'GET',
headers: {'Authorization': 'token ' + credentials, 'User-Agent': 'curly'}
}, function (response) {
var responseData = '';
response.setEncoding('utf8');
response.on('data', function (chunk) {
responseData += chunk;
});
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
}).end();
} else {
console.err("The token is not a Bearer");
res.send(401)
}
}
} else {
res.send(401, {error: "Full authentication is necessary to access this resource"})
}
};
The policy is called once I hit the controller route but it throws a _http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
And the process is terminate.
The problem I think is the next() and the returns I tried everywhere I think, to put the next() call, but still gives me this error, if I remove then I lock the request on the policy.
EDIT
I did a simple sample of policy where I just set some property on req.options.values and happened the same problem, so maybe could be an issue with req.options.requestData = JSON.parse(responseData); ? How else could I set a property to send to controller ?
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
both are getting executed.to check console.log("something") in error to see if there is error.
This happens when you're trying to modify the request and response together or modify one of them twice.
In your code, I think the callback is being called twice and you are also modifying the response at the same time. Check the lines where you're calling callback "next()". You'll find your issue.

Using supertest to check didFlash after a redirect

I'm trying to test what happens when the user destroy callback receives an error for the user controller. When destroy receives an error, it does the following:
flash('error', 'Can not destroy user');
redirect(path_to.users);
This is the test so far:
it('should fail on DELETE /users/:id if destroy receives an error', function (done) {
var User = app.models.User;
var user = new UserStub();
User.find = sinon.spy(function (id, callback) {
callback(null, user);
});
user.destroy = sinon.spy(function (callback) {
callback(new Error());
});
request(app)
.del('/users/55')
.end(function (err, res) {
res.header.location.should.include('/users');
app.didFlash('error').should.be.true;
done();
});
});
I've seen this question and the res.header.. portion works as expected. However, I'm still confused on how I can test the flash that happens after that redirect.
I ended up changing the users_controller to use the following code for a destroy callback (the redirect was having other issues):
if (error) {
flash('error', 'Can not destroy user');
} else {
flash('info', 'User successfully removed');
}
send(302, "'" + pathTo.users + "'");
The init.js file used with mocha.js has a few pieces in it when initializing the app object (some irrelevant code was omitted):
global.getApp = function(done) {
var app = require('compound').createServer();
app.renderedViews = [];
app.flashedMessages = {};
app._render = app.render;
app.render = function (viewName, opts, fn) {
app.renderedViews.push(viewName);
// Deep-copy flash messages
var flashes = opts.request.session.flash;
for(var type in flashes) {
app.flashedMessages[type] = [];
for(var i in flashes[type]) {
app.flashedMessages[type].push(flashes[type][i]);
}
}
return app._render.apply(this, arguments);
};
app.use(function (req, res, next) {
app._request = req;
next();
});
app.didFlash = function (type) {
var flashes = app._request.session.flash;
return !!(app.flashedMessages[type] || (flashes && flashes[type]));
};
return app;
};
The original way of checking for didFlash was limited to only rendering, but this checks if a flash message is created before a redirect or send.

Invoking an asynchronous method inside a middleware in node-http-proxy

I'm trying to create a proxy with node-http-proxy in Node.js that checks whether a request is authorized in a mongodb.
Basically, I created a middleware module for the node-http-proxy that I use like this:
httpProxy.createServer(
require('./example-middleware')(),
9005, 'localhost'
).listen(8005)
What the middleware module does is using mongojs to connect to mongodb and run a query to see if the user is authorized to access the resource:
module.exports = function(){
// Do something when first loaded!
console.log("Middleware loaded!");
return function (req, res, next) {
var record = extractCredentials(req);
var query = -- Db query --
//Debug:
log("Query result", query);
db.authList.find(query).sort({
"url.len": -1
}, function(err, docs){
console.log(docs);
// Return the creator for the longest matching path:
if(docs.length > 0) {
console.log("User confirmed!");
next();
} else {
console.log("User not confirmed!");
res.writeHead(403, {
'Content-Type': 'text/plain'
});
res.write('You are not allowed to access this resource...');
res.end();
}
});
}
}
Now the problem is that as soon as I add the asynchronous call to mongodb using mongojs the proxy hangs and never send the response back.
To clarify: on a "User not confirmed" everything works fine and the 403 is returned. On a "user confirmed" however I see the log but the browser then hangs forever and the request isn't proxied.
Now, if I remove the "user confirmed" and next() part outside of a callback it does work:
module.exports = function(){
// Do something when first loaded!
console.log("Middleware loaded!");
return function (req, res, next) {
var record = extractCredentials(req);
var query = --- query ---
console.log("User confirmed!");
next();
}
but I can't do that since the mongojs query is meant (rightfully I guess) to be executed asynchronously, the callback being triggered only when the db replied...
I also tried the version without using a middleware:
http.createServer(function (req, res) {
// run the async query here!
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9000
});
}).listen(8001);
But that did not help either...
Any clue? Note that I'm new to node.js so I suspect a misunderstanding on my side...
Found the answer, actually the catch is that the request needs to be buffered:
httpProxy.createServer(function (req, res, proxy) {
// ignore favicon
if (req.url === '/favicon.ico') {
res.writeHead(200, {
'Content-Type': 'image/x-icon'
} );
res.end();
console.log('favicon requested');
return;
}
var credentials = extractCredentials(req);
console.log(credentials);
var buffer = httpProxy.buffer(req);
checkRequest(credentials, function(user){
if(user == ...) {
console.log("Access granted!");
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9005,
buffer: buffer
});
} else {
console.log("Access denied!");
res.writeHead(403, {
"Content-Type": "text/plain"
});
res.write("You are not allowed to access this resource...");
res.end();
}
});
}).listen(8005);
Two problems:
You're not calling next(); in the else case of your sort callback.
The second parameter to your sort callback is a Cursor, not an array of documents. As such, docs.length > 0 is never true and the code always follows the else path.

Resources