| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 15:26:19 -06:00
										 |  |  | module.exports.create = function (deps, conf) { | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |   var PromiseA = require('bluebird'); | 
					
						
							|  |  |  |   var request = PromiseA.promisify(require('request')); | 
					
						
							|  |  |  |   var pending = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |   async function checkPublicAddr(host) { | 
					
						
							|  |  |  |     var result = await request({ | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |       method: 'GET' | 
					
						
							| 
									
										
										
										
											2017-06-27 10:39:59 -06:00
										 |  |  |     , url: host+'/api/org.oauth3.tunnel/checkip' | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |     , json: true | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!result.body) { | 
					
						
							|  |  |  |       throw new Error('No response body in request for public address'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (result.body.error) { | 
					
						
							|  |  |  |       // Note that the error on the body will probably have a message that overwrites the default
 | 
					
						
							|  |  |  |       throw Object.assign(new Error('error in check IP response'), result.body.error); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result.body.address; | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |   async function checkSinglePort(host, address, port) { | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |     var crypto = require('crypto'); | 
					
						
							|  |  |  |     var token   = crypto.randomBytes(8).toString('hex'); | 
					
						
							|  |  |  |     var keyAuth = crypto.randomBytes(32).toString('hex'); | 
					
						
							|  |  |  |     pending[token] = keyAuth; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     var reqObj = { | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |       method: 'POST' | 
					
						
							| 
									
										
										
										
											2017-06-27 10:39:59 -06:00
										 |  |  |     , url: host+'/api/org.oauth3.tunnel/loopback' | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     , json: { | 
					
						
							|  |  |  |         address: address | 
					
						
							|  |  |  |       , port: port | 
					
						
							|  |  |  |       , token: token | 
					
						
							|  |  |  |       , keyAuthorization: keyAuth | 
					
						
							|  |  |  |       , iat: Date.now() | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var result; | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       result = await request(reqObj); | 
					
						
							|  |  |  |     } catch (err) { | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |       delete pending[token]; | 
					
						
							|  |  |  |       throw err; | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete pending[token]; | 
					
						
							|  |  |  |     if (!result.body) { | 
					
						
							|  |  |  |       throw new Error('No response body in loopback request for port '+port); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // If the loopback requests don't go to us then there are all kinds of ways it could
 | 
					
						
							|  |  |  |     // error, but none of them really provide much extra information so we don't do
 | 
					
						
							|  |  |  |     // anything that will break the PromiseA.all out and mask the other results.
 | 
					
						
							|  |  |  |     if (conf.debug && result.body.error) { | 
					
						
							|  |  |  |       console.log('error on remote side of port '+port+' loopback', result.body.error); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return !!result.body.success; | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |   async function loopback(provider) { | 
					
						
							|  |  |  |     var directives = await deps.OAUTH3.discover(provider); | 
					
						
							|  |  |  |     var address = await checkPublicAddr(directives.api); | 
					
						
							|  |  |  |     console.log('checking to see if', address, 'gets back to us'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var ports = require('./servers').listeners.tcp.list(); | 
					
						
							|  |  |  |     var values = await PromiseA.all(ports.map(function (port) { | 
					
						
							|  |  |  |       return checkSinglePort(directives.api, address, port); | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (conf.debug) { | 
					
						
							|  |  |  |       console.log('remaining loopback tokens', pending); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-09-14 15:26:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     var result = {error: null, address: address}; | 
					
						
							|  |  |  |     ports.forEach(function (port, ind) { | 
					
						
							|  |  |  |       result[port] = values[ind]; | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-09-19 13:18:22 -06:00
										 |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 15:26:19 -06:00
										 |  |  |   loopback.checkPublicAddr = checkPublicAddr; | 
					
						
							| 
									
										
										
										
											2017-06-26 11:34:42 -06:00
										 |  |  |   loopback.server = require('http').createServer(function (req, res) { | 
					
						
							|  |  |  |     var parsed = require('url').parse(req.url); | 
					
						
							|  |  |  |     var token = parsed.pathname.replace('/.well-known/cloud-challenge/', ''); | 
					
						
							|  |  |  |     if (pending[token]) { | 
					
						
							|  |  |  |       res.setHeader('Content-Type', 'text/plain'); | 
					
						
							|  |  |  |       res.end(pending[token]); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       res.statusCode = 404; | 
					
						
							|  |  |  |       res.end(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return loopback; | 
					
						
							|  |  |  | }; |