forked from coolaj86/goldilocks.js
		
	updated the DDNS and loopback to use async/await
This commit is contained in:
		
							parent
							
								
									a625ee9db9
								
							
						
					
					
						commit
						cfcc1acb8c
					
				
							
								
								
									
										231
									
								
								lib/ddns.js
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								lib/ddns.js
									
									
									
									
									
								
							| @ -16,145 +16,126 @@ module.exports.create = function (deps, conf) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function setDeviceAddress(addr) { |   async function getSession() { | ||||||
|     return deps.storage.owners.all().then(function (sessions) { |     var sessions = await deps.storage.owners.all(); | ||||||
|       return sessions.filter(function (sess) { |     var session = sessions.filter(function (sess) { | ||||||
|         return sess.token.scp.indexOf('dns') >= 0; |       return sess.token.scp.indexOf('dns') >= 0; | ||||||
|       })[0]; |     })[0]; | ||||||
|     }).then(function (session) { |  | ||||||
|       if (!session) { |  | ||||||
|         return PromiseA.reject(new Error('no sessions with DNS grants')); |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       // The OAUTH3 library stores some things on the root session object that we usually
 |     if (!session) { | ||||||
|       // just leave inside the token, but we need to pull those out before we use it here
 |       throw new Error('no sessions with DNS grants'); | ||||||
|       session.provider_uri = session.provider_uri || session.token.provider_uri || session.token.iss; |     } | ||||||
|       session.client_uri = session.client_uri || session.token.azp; |  | ||||||
|       session.scope = session.scope || session.token.scp; |  | ||||||
| 
 | 
 | ||||||
|       return OAUTH3.discover(session.token.aud).then(function (directives) { |     // The OAUTH3 library stores some things on the root session object that we usually
 | ||||||
|         return request({ |     // just leave inside the token, but we need to pull those out before we use it here
 | ||||||
|           url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname |     session.provider_uri = session.provider_uri || session.token.provider_uri || session.token.iss; | ||||||
|         , method: 'POST' |     session.client_uri = session.client_uri || session.token.azp; | ||||||
|         , headers: { |     session.scope = session.scope || session.token.scp; | ||||||
|             'Authorization': 'Bearer ' + session.refresh_token |     return session; | ||||||
|           , 'Accept': 'application/json; charset=utf-8' |  | ||||||
|           } |  | ||||||
|         , json: { |  | ||||||
|             addresses: [ |  | ||||||
|               { value: addr, type:  dnsType(addr) } |  | ||||||
|             ] |  | ||||||
|           } |  | ||||||
|         }).then(function () { |  | ||||||
|           return OAUTH3.api(directives.api, {session: session, api: 'dns.list'}).then(function (list) { |  | ||||||
|             return list.filter(function (record) { |  | ||||||
|               return record.device === conf.device.hostname; |  | ||||||
|             }).map(function (record) { |  | ||||||
|               var split = record.zone.split('.'); |  | ||||||
|               return { |  | ||||||
|                 tld: split.slice(1).join('.') |  | ||||||
|               , sld: split[0] |  | ||||||
|               , sub: record.host.slice(0, -(record.zone.length + 1)) |  | ||||||
|               }; |  | ||||||
|             }); |  | ||||||
|           }); |  | ||||||
|         }).then(function (domains) { |  | ||||||
|           var common = { |  | ||||||
|             api: 'devices.detach' |  | ||||||
|           , session: session |  | ||||||
|           , device: conf.device.hostname |  | ||||||
|           }; |  | ||||||
| 
 |  | ||||||
|           return PromiseA.all(domains.map(function (record) { |  | ||||||
|             return OAUTH3.api(directives.api, Object.assign({}, common, record)); |  | ||||||
|           })).then(function () { |  | ||||||
|             return domains; |  | ||||||
|           }); |  | ||||||
|         }).then(function (domains) { |  | ||||||
|           var common = { |  | ||||||
|             api: 'devices.attach' |  | ||||||
|           , session: session |  | ||||||
|           , device: conf.device.hostname |  | ||||||
|           , ip: addr |  | ||||||
|           , ttl: 300 |  | ||||||
|           }; |  | ||||||
| 
 |  | ||||||
|           return PromiseA.all(domains.map(function (record) { |  | ||||||
|             return OAUTH3.api(directives.api, Object.assign({}, common, record)); |  | ||||||
|           })); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function getDeviceAddresses() { |   async function setDeviceAddress(addr) { | ||||||
|     return deps.storage.owners.all().then(function (sessions) { |     var session = await getSession(); | ||||||
|       return sessions.filter(function (sess) { |     var directives = await OAUTH3.discover(session.token.aud); | ||||||
|         return sess.token.scp.indexOf('dns') >= 0; |  | ||||||
|       })[0]; |  | ||||||
|     }).then(function (session) { |  | ||||||
|       if (!session) { |  | ||||||
|         return PromiseA.reject(new Error('no sessions with DNS grants')); |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       return OAUTH3.discover(session.token.aud).then(function (directives) { |     // Set the address of the device to our public address.
 | ||||||
|         return request({ |     await request({ | ||||||
|           url: directives.api+'/api/org.oauth3.dns/acl/devices' |       url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname | ||||||
|         , method: 'GET' |     , method: 'POST' | ||||||
|         , headers: { |     , headers: { | ||||||
|             'Authorization': 'Bearer ' + session.refresh_token |         'Authorization': 'Bearer ' + session.refresh_token | ||||||
|           , 'Accept': 'application/json; charset=utf-8' |       , 'Accept': 'application/json; charset=utf-8' | ||||||
|           } |       } | ||||||
|         , json: true |     , json: { | ||||||
|         }); |         addresses: [ | ||||||
|       }).then(function (result) { |           { value: addr, type:  dnsType(addr) } | ||||||
|         if (!result.body) { |         ] | ||||||
|           return PromiseA.reject(new Error('No response body in request for device addresses')); |       } | ||||||
|         } |  | ||||||
|         if (result.body.error) { |  | ||||||
|           var err = new Error(result.body.error.message); |  | ||||||
|           return PromiseA.reject(Object.assign(err, result.body.error)); |  | ||||||
|         } |  | ||||||
|         return result.body.devices.filter(function (dev) { |  | ||||||
|           return dev.name === conf.device.hostname; |  | ||||||
|         })[0]; |  | ||||||
|       }).then(function (dev) { |  | ||||||
|         return (dev || {}).addresses || []; |  | ||||||
|       }); |  | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     // Then update all of the records attached to our hostname, first removing the old records
 | ||||||
|  |     // to remove the reference to the old address, then creating new records for the same domains
 | ||||||
|  |     // using our new address.
 | ||||||
|  |     var allDns = OAUTH3.api(directives.api, {session: session, api: 'dns.list'}); | ||||||
|  |     var ourDomains = allDns.filter(function (record) { | ||||||
|  |       return record.device === conf.device.hostname; | ||||||
|  |     }).map(function (record) { | ||||||
|  |       var zoneSplit = record.zone.split('.'); | ||||||
|  |       return { | ||||||
|  |         tld: zoneSplit.slice(1).join('.') | ||||||
|  |       , sld: zoneSplit[0] | ||||||
|  |       , sub: record.host.slice(0, -(record.zone.length + 1)) | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     var common = { | ||||||
|  |       api: 'devices.detach' | ||||||
|  |     , session: session | ||||||
|  |     , device: conf.device.hostname | ||||||
|  |     }; | ||||||
|  |     await PromiseA.all(ourDomains.map(function (record) { | ||||||
|  |       return OAUTH3.api(directives.api, Object.assign({}, common, record)); | ||||||
|  |     })); | ||||||
|  | 
 | ||||||
|  |     common = { | ||||||
|  |       api: 'devices.attach' | ||||||
|  |     , session: session | ||||||
|  |     , device: conf.device.hostname | ||||||
|  |     , ip: addr | ||||||
|  |     , ttl: 300 | ||||||
|  |     }; | ||||||
|  |     await PromiseA.all(ourDomains.map(function (record) { | ||||||
|  |       return OAUTH3.api(directives.api, Object.assign({}, common, record)); | ||||||
|  |     })); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async function getDeviceAddresses() { | ||||||
|  |     var session = await getSession(); | ||||||
|  |     var directives = await OAUTH3.discover(session.token.aud); | ||||||
|  | 
 | ||||||
|  |     var result = await request({ | ||||||
|  |       url: directives.api+'/api/org.oauth3.dns/acl/devices' | ||||||
|  |     , method: 'GET' | ||||||
|  |     , headers: { | ||||||
|  |         'Authorization': 'Bearer ' + session.refresh_token | ||||||
|  |       , 'Accept': 'application/json; charset=utf-8' | ||||||
|  |       } | ||||||
|  |     , json: true | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (!result.body) { | ||||||
|  |       throw new Error('No response body in request for device addresses'); | ||||||
|  |     } | ||||||
|  |     if (result.body.error) { | ||||||
|  |       throw Object.assign(new Error('error getting device list'), result.body.error); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var dev = result.body.devices.filter(function (dev) { | ||||||
|  |       return dev.name === conf.device.hostname; | ||||||
|  |     })[0]; | ||||||
|  |     return (dev || {}).addresses || []; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var publicAddress; |   var publicAddress; | ||||||
|   function recheckPubAddr() { |   async function recheckPubAddr() { | ||||||
|     if (!conf.ddns.enabled) { |     if (!conf.ddns.enabled) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     deps.storage.owners.all().then(function (sessions) { |     var session = await getSession(); | ||||||
|       return sessions.filter(function (sess) { |     var directives = await OAUTH3.discover(session.token.aud); | ||||||
|         return sess.token.scp.indexOf('dns') >= 0; |     var addr = await deps.loopback.checkPublicAddr(directives.api); | ||||||
|       })[0]; |  | ||||||
|     }).then(function (session) { |  | ||||||
|       if (!session) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       OAUTH3.discover(session.token.aud).then(function (directives) { |     if (publicAddress === addr) { | ||||||
|         return deps.loopback.checkPublicAddr(directives.api); |       return; | ||||||
|       }).then(function (addr) { |     } | ||||||
|         if (publicAddress !== addr) { | 
 | ||||||
|           if (conf.debug) { |     if (conf.debug) { | ||||||
|             console.log('previous public address',publicAddress, 'does not match current public address', addr); |       console.log('previous public address',publicAddress, 'does not match current public address', addr); | ||||||
|           } |     } | ||||||
|           publicAddress = addr; | 
 | ||||||
|           setDeviceAddress(addr); |     await setDeviceAddress(addr); | ||||||
|         } |     publicAddress = addr; | ||||||
|       }, function (err) { |  | ||||||
|         if (conf.debug) { |  | ||||||
|           console.error('error getting public address', err); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   recheckPubAddr(); |   recheckPubAddr(); | ||||||
|  | |||||||
							
								
								
									
										110
									
								
								lib/loopback.js
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								lib/loopback.js
									
									
									
									
									
								
							| @ -5,81 +5,81 @@ module.exports.create = function (deps, conf) { | |||||||
|   var request = PromiseA.promisify(require('request')); |   var request = PromiseA.promisify(require('request')); | ||||||
|   var pending = {}; |   var pending = {}; | ||||||
| 
 | 
 | ||||||
|   function checkPublicAddr(host) { |   async function checkPublicAddr(host) { | ||||||
|     return request({ |     var result = await request({ | ||||||
|       method: 'GET' |       method: 'GET' | ||||||
|     , url: host+'/api/org.oauth3.tunnel/checkip' |     , url: host+'/api/org.oauth3.tunnel/checkip' | ||||||
|     , json: true |     , json: true | ||||||
|     }).then(function (result) { |  | ||||||
|       if (!result.body) { |  | ||||||
|         return PromiseA.reject(new Error('No response body in request for public address')); |  | ||||||
|       } |  | ||||||
|       if (result.body.error) { |  | ||||||
|         var err = new Error(result.body.error.message); |  | ||||||
|         return PromiseA.reject(Object.assign(err, result.body.error)); |  | ||||||
|       } |  | ||||||
|       return result.body.address; |  | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function checkSinglePort(host, address, port) { |   async function checkSinglePort(host, address, port) { | ||||||
|     var crypto = require('crypto'); |     var crypto = require('crypto'); | ||||||
|     var token   = crypto.randomBytes(8).toString('hex'); |     var token   = crypto.randomBytes(8).toString('hex'); | ||||||
|     var keyAuth = crypto.randomBytes(32).toString('hex'); |     var keyAuth = crypto.randomBytes(32).toString('hex'); | ||||||
|     pending[token] = keyAuth; |     pending[token] = keyAuth; | ||||||
| 
 | 
 | ||||||
|     var opts = { |     var reqObj = { | ||||||
|       address: address |  | ||||||
|     , port: port |  | ||||||
|     , token: token |  | ||||||
|     , keyAuthorization: keyAuth |  | ||||||
|     , iat: Date.now() |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     return request({ |  | ||||||
|       method: 'POST' |       method: 'POST' | ||||||
|     , url: host+'/api/org.oauth3.tunnel/loopback' |     , url: host+'/api/org.oauth3.tunnel/loopback' | ||||||
|     , json: opts |     , json: { | ||||||
|     }) |         address: address | ||||||
|     .then(function (result) { |       , port: port | ||||||
|       delete pending[token]; |       , token: token | ||||||
|       if (!result.body) { |       , keyAuthorization: keyAuth | ||||||
|         return PromiseA.reject(new Error('No response body in loopback request for port '+port)); |       , iat: Date.now() | ||||||
|       } |       } | ||||||
|       // 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.
 |     var result; | ||||||
|       if (conf.debug && result.body.error) { |     try { | ||||||
|         console.log('error on remote side of port '+port+' loopback', result.body.error); |       result = await request(reqObj); | ||||||
|       } |     } catch (err) { | ||||||
|       return !!result.body.success; |  | ||||||
|     }, function (err) { |  | ||||||
|       delete pending[token]; |       delete pending[token]; | ||||||
|       throw err; |       throw err; | ||||||
|     }); |     } | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function loopback(provider) { |   async function loopback(provider) { | ||||||
|     return deps.OAUTH3.discover(provider).then(function (directives) { |     var directives = await deps.OAUTH3.discover(provider); | ||||||
|       return checkPublicAddr(directives.api).then(function (address) { |     var address = await checkPublicAddr(directives.api); | ||||||
|         console.log('checking to see if', address, 'gets back to us'); |     console.log('checking to see if', address, 'gets back to us'); | ||||||
|         var ports = require('./servers').listeners.tcp.list(); |  | ||||||
|         return PromiseA.all(ports.map(function (port) { |  | ||||||
|           return checkSinglePort(directives.api, address, port); |  | ||||||
|         })) |  | ||||||
|         .then(function (values) { |  | ||||||
|           if (conf.debug) { |  | ||||||
|             console.log('remaining loopback tokens', pending); |  | ||||||
|           } |  | ||||||
| 
 | 
 | ||||||
|           var result = {error: null, address: address}; |     var ports = require('./servers').listeners.tcp.list(); | ||||||
|           ports.forEach(function (port, ind) { |     var values = await PromiseA.all(ports.map(function (port) { | ||||||
|             result[port] = values[ind]; |       return checkSinglePort(directives.api, address, port); | ||||||
|           }); |     })); | ||||||
|           return result; | 
 | ||||||
|         }); |     if (conf.debug) { | ||||||
|       }); |       console.log('remaining loopback tokens', pending); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var result = {error: null, address: address}; | ||||||
|  |     ports.forEach(function (port, ind) { | ||||||
|  |       result[port] = values[ind]; | ||||||
|     }); |     }); | ||||||
|  |     return result; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   loopback.checkPublicAddr = checkPublicAddr; |   loopback.checkPublicAddr = checkPublicAddr; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user