'use strict'; module.exports.create = function (deps, conf) { var PromiseA = deps.PromiseA; var request = PromiseA.promisify(require('request')); var OAUTH3 = require('../packages/assets/org.oauth3'); require('../packages/assets/org.oauth3/oauth3.dns.js'); OAUTH3._hooks = require('../packages/assets/org.oauth3/oauth3.node.storage.js'); function dnsType(addr) { if (/^\d+\.\d+\.\d+\.\d+$/.test(addr)) { return 'A'; } if (-1 !== addr.indexOf(':') && /^[a-f:\.\d]+$/i.test(addr)) { return 'AAAA'; } } 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')); } // 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 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)); })); }); }); }); } 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')); } 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 || []; }); }); } var publicAddress; 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; } 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); } }); }); } recheckPubAddr(); setInterval(recheckPubAddr, 5*60*1000); return { setDeviceAddress: setDeviceAddress , getDeviceAddresses: getDeviceAddresses , recheckPubAddr: recheckPubAddr }; };