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) {
 | 
			
		||||
    return deps.storage.owners.all().then(function (sessions) {
 | 
			
		||||
      return sessions.filter(function (sess) {
 | 
			
		||||
        return sess.token.scp.indexOf('dns') >= 0;
 | 
			
		||||
      })[0];
 | 
			
		||||
    }).then(function (session) {
 | 
			
		||||
      if (!session) {
 | 
			
		||||
        return PromiseA.reject(new Error('no sessions with DNS grants'));
 | 
			
		||||
      }
 | 
			
		||||
  async function getSession() {
 | 
			
		||||
    var sessions = await deps.storage.owners.all();
 | 
			
		||||
    var session = sessions.filter(function (sess) {
 | 
			
		||||
      return sess.token.scp.indexOf('dns') >= 0;
 | 
			
		||||
    })[0];
 | 
			
		||||
 | 
			
		||||
      // The OAUTH3 library stores some things on the root session object that we usually
 | 
			
		||||
      // just leave inside the token, but we need to pull those out before we use it here
 | 
			
		||||
      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;
 | 
			
		||||
    if (!session) {
 | 
			
		||||
      throw new Error('no sessions with DNS grants');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.discover(session.token.aud).then(function (directives) {
 | 
			
		||||
        return request({
 | 
			
		||||
          url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname
 | 
			
		||||
        , method: 'POST'
 | 
			
		||||
        , headers: {
 | 
			
		||||
            'Authorization': 'Bearer ' + session.refresh_token
 | 
			
		||||
          , '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));
 | 
			
		||||
          }));
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    // The OAUTH3 library stores some things on the root session object that we usually
 | 
			
		||||
    // just leave inside the token, but we need to pull those out before we use it here
 | 
			
		||||
    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 session;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getDeviceAddresses() {
 | 
			
		||||
    return deps.storage.owners.all().then(function (sessions) {
 | 
			
		||||
      return sessions.filter(function (sess) {
 | 
			
		||||
        return sess.token.scp.indexOf('dns') >= 0;
 | 
			
		||||
      })[0];
 | 
			
		||||
    }).then(function (session) {
 | 
			
		||||
      if (!session) {
 | 
			
		||||
        return PromiseA.reject(new Error('no sessions with DNS grants'));
 | 
			
		||||
      }
 | 
			
		||||
  async function setDeviceAddress(addr) {
 | 
			
		||||
    var session = await getSession();
 | 
			
		||||
    var directives = await OAUTH3.discover(session.token.aud);
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.discover(session.token.aud).then(function (directives) {
 | 
			
		||||
        return 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
 | 
			
		||||
        });
 | 
			
		||||
      }).then(function (result) {
 | 
			
		||||
        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 || [];
 | 
			
		||||
      });
 | 
			
		||||
    // Set the address of the device to our public address.
 | 
			
		||||
    await request({
 | 
			
		||||
      url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname
 | 
			
		||||
    , method: 'POST'
 | 
			
		||||
    , headers: {
 | 
			
		||||
        'Authorization': 'Bearer ' + session.refresh_token
 | 
			
		||||
      , 'Accept': 'application/json; charset=utf-8'
 | 
			
		||||
      }
 | 
			
		||||
    , json: {
 | 
			
		||||
        addresses: [
 | 
			
		||||
          { value: addr, type:  dnsType(addr) }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
  function recheckPubAddr() {
 | 
			
		||||
  async function recheckPubAddr() {
 | 
			
		||||
    if (!conf.ddns.enabled) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    deps.storage.owners.all().then(function (sessions) {
 | 
			
		||||
      return sessions.filter(function (sess) {
 | 
			
		||||
        return sess.token.scp.indexOf('dns') >= 0;
 | 
			
		||||
      })[0];
 | 
			
		||||
    }).then(function (session) {
 | 
			
		||||
      if (!session) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    var session = await getSession();
 | 
			
		||||
    var directives = await OAUTH3.discover(session.token.aud);
 | 
			
		||||
    var addr = await deps.loopback.checkPublicAddr(directives.api);
 | 
			
		||||
 | 
			
		||||
      OAUTH3.discover(session.token.aud).then(function (directives) {
 | 
			
		||||
        return deps.loopback.checkPublicAddr(directives.api);
 | 
			
		||||
      }).then(function (addr) {
 | 
			
		||||
        if (publicAddress !== addr) {
 | 
			
		||||
          if (conf.debug) {
 | 
			
		||||
            console.log('previous public address',publicAddress, 'does not match current public address', addr);
 | 
			
		||||
          }
 | 
			
		||||
          publicAddress = addr;
 | 
			
		||||
          setDeviceAddress(addr);
 | 
			
		||||
        }
 | 
			
		||||
      }, function (err) {
 | 
			
		||||
        if (conf.debug) {
 | 
			
		||||
          console.error('error getting public address', err);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    if (publicAddress === addr) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (conf.debug) {
 | 
			
		||||
      console.log('previous public address',publicAddress, 'does not match current public address', addr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await setDeviceAddress(addr);
 | 
			
		||||
    publicAddress = addr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 pending = {};
 | 
			
		||||
 | 
			
		||||
  function checkPublicAddr(host) {
 | 
			
		||||
    return request({
 | 
			
		||||
  async function checkPublicAddr(host) {
 | 
			
		||||
    var result = await request({
 | 
			
		||||
      method: 'GET'
 | 
			
		||||
    , url: host+'/api/org.oauth3.tunnel/checkip'
 | 
			
		||||
    , 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 token   = crypto.randomBytes(8).toString('hex');
 | 
			
		||||
    var keyAuth = crypto.randomBytes(32).toString('hex');
 | 
			
		||||
    pending[token] = keyAuth;
 | 
			
		||||
 | 
			
		||||
    var opts = {
 | 
			
		||||
      address: address
 | 
			
		||||
    , port: port
 | 
			
		||||
    , token: token
 | 
			
		||||
    , keyAuthorization: keyAuth
 | 
			
		||||
    , iat: Date.now()
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return request({
 | 
			
		||||
    var reqObj = {
 | 
			
		||||
      method: 'POST'
 | 
			
		||||
    , url: host+'/api/org.oauth3.tunnel/loopback'
 | 
			
		||||
    , json: opts
 | 
			
		||||
    })
 | 
			
		||||
    .then(function (result) {
 | 
			
		||||
      delete pending[token];
 | 
			
		||||
      if (!result.body) {
 | 
			
		||||
        return PromiseA.reject(new Error('No response body in loopback request for port '+port));
 | 
			
		||||
    , json: {
 | 
			
		||||
        address: address
 | 
			
		||||
      , port: port
 | 
			
		||||
      , token: token
 | 
			
		||||
      , keyAuthorization: keyAuth
 | 
			
		||||
      , 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.
 | 
			
		||||
      if (conf.debug && result.body.error) {
 | 
			
		||||
        console.log('error on remote side of port '+port+' loopback', result.body.error);
 | 
			
		||||
      }
 | 
			
		||||
      return !!result.body.success;
 | 
			
		||||
    }, function (err) {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var result;
 | 
			
		||||
    try {
 | 
			
		||||
      result = await request(reqObj);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      delete pending[token];
 | 
			
		||||
      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) {
 | 
			
		||||
    return deps.OAUTH3.discover(provider).then(function (directives) {
 | 
			
		||||
      return checkPublicAddr(directives.api).then(function (address) {
 | 
			
		||||
        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);
 | 
			
		||||
          }
 | 
			
		||||
  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 result = {error: null, address: address};
 | 
			
		||||
          ports.forEach(function (port, ind) {
 | 
			
		||||
            result[port] = values[ind];
 | 
			
		||||
          });
 | 
			
		||||
          return result;
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var result = {error: null, address: address};
 | 
			
		||||
    ports.forEach(function (port, ind) {
 | 
			
		||||
      result[port] = values[ind];
 | 
			
		||||
    });
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loopback.checkPublicAddr = checkPublicAddr;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user