diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/lib/index.js b/lib/index.js index 2995a01..bb051a8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -11,108 +11,41 @@ module.exports.create = function(config) { // config = { baseUrl, token } var baseUrl = config.baseUrl || defaults.baseUrl; var authtoken = config.token; - return { - set: function(data) { - var ch = data.challenge; - var domainname = ch.identifier.value; - // PROBLEM: zone != domain - // example.com, foo.example.com, and bar.foo.example.com are all in the example.com zone!! - // Need to get list of "domains" (zones) and *then* set "subdomains" (domain records) - // https://developers.digitalocean.com/documentation/v2/#domains - var zone = domainname; - // PROBLEM: dnsPrefix != dnsHost.split('.')[0] - // _greenlock-dryrun-2277.bar.foo.example.com => _greenlock-dryrun-2277.bar.foo - var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); - var txt = ch.dnsAuthorization; - // If the domain to be verified is - var url = baseUrl + '/v2/domains/' + zone + '/records'; - console.log('adding txt', data); + var helpers = { + getDomain: function(identifier) { + var url = baseUrl + '/v2/domains/'; + return request({ - method: 'POST', + method: 'GET', url: url, headers: { Authorization: 'Bearer ' + authtoken, 'Content-Type': 'application/json' }, - json: { - type: 'TXT', - name: dnsPrefix, - data: txt, - // PROBLEM (fixed) set a LOW ttl so that responses are not cached so long - ttl: 300 - } - }).then(function(resp) { - resp = resp.body; - console.log(resp); - if (resp && resp.domain_record && resp.domain_record.data === txt) { - return true; - } - throw new Error('record did not set. check subdomain, api key, etc'); - }); - }, - remove: function(data) { - var domainname = data.challenge.altname; - var zone = domainname; - // PROBLEM: domainname != zone - var url = baseUrl + '/v2/domains/' + zone + '/records'; - - // Digital ocean provides the api to remove records by ID. Since we do not have id, we fetch all the records, - // filter the required TXT record and pass its id to remove API - return request({ - method: 'GET', - url: url, - // PROBLEM (fixed): Remember to set json: true (not need to JSON.parse) - json: true, - headers: { - Authorization: 'Bearer ' + authtoken, - 'Content-Type': 'application/json' - } + json: true }) .then(function(resp) { - resp = resp.body; - var entries = - resp && - resp.domain_records && - resp.domain_records.filter(function(x) { - return x.type === 'TXT'; - }); - // PROBLEM entry[0] !== our entry - // (see solution for other entries, down below) - if (entries.length > 0) { - return entries[0].id; - } else { - throw new Error( - 'Couldnt remove record. check subdomain, api key, etc' - ); - } - }) - .then(function(recordId) { - var domainname = data.challenge.altname; - var zone = domainname; - var url = baseUrl + '/v2/domains/' + zone + '/records/' + recordId; - - return request({ - method: 'DELETE', - url: url, - headers: { - Authorization: 'Bearer ' + authtoken, - 'Content-Type': 'application/json' - } - }).then(function(resp) { - resp = resp.body; - console.log(resp); - return true; + return resp.body.domains.map(function(x) { + return x.name; }); - }); + }) + .then(function(domains) { + for (var i = 0; i < domains.length; i++) { + var domain = domains[i]; + var pattern = new RegExp(domain.replace('.', '.') + '$'); + if (pattern.test(identifier)) { + return domain; + } + } + throw new Error('Domain not found for: ' + identifier); + }) }, - get: function(data) { - var ch = data.challenge; - var domainname = data.challenge.altname; - var zone = domainname; - // PROBLEM: domainname != zone - var url = baseUrl + '/v2/domains/' + zone + '/records'; - console.log('getting txt', data); + getTXTRecord: function(data) { + // data:{dnsPrefix:"_88-acme-challenge-0e.foo",zone:"example.com",txt:"_cdZWaclIbkP1qYpMkZIURTK--ydQIK6d9axFmftWz0"} + var dnsPrefix = data.dnsPrefix; + var txt = data.txt; + var url = baseUrl + '/v2/domains/' + data.zone + '/records'; // Digital ocean provides the api to fetch records by ID. Since we do not have id, we fetch all the records, // filter the required TXT record @@ -132,20 +65,113 @@ module.exports.create = function(config) { resp && resp.domain_records && resp.domain_records.filter(function(x) { - // PROBLEM should also check for prefix - return x.type === 'TXT'; + return x.type === 'TXT' && x.name === dnsPrefix && x.data === txt; }); - // PROBLEM (fixed): entries[0] !== our entry - var entry = entries.filter(function(x) { - console.log('data', x.data); - console.log('dnsAuth', ch.dnsAuthorization, ch); - return x.data === ch.dnsAuthorization; - })[0]; - if (entry) { - return { dnsAuthorization: entry.data }; - } else { - return null; - } + return entries && entries[0]; + }); + } + }; + + return { + set: function(data) { + var ch = data.challenge; + var domainRecord = ch.identifier.value; + + // Note: zone != domain + // example.com, foo.example.com, and bar.foo.example.com are all in the example.com zone!! + // Need to get list of "domains" (zones) and *then* set "subdomains" (domain records) + // https://developers.digitalocean.com/documentation/v2/#domains + return helpers.getDomain(domainRecord).then(function(zone) { + // Note: dnsPrefix != dnsHost.split('.')[0] + // _greenlock-dryrun-2277.bar.foo.example.com => _greenlock-dryrun-2277.bar.foo + var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); + var txt = ch.dnsAuthorization; + var url = baseUrl + '/v2/domains/' + zone + '/records'; + + console.info('Adding TXT', data); + return request({ + method: 'POST', + url: url, + headers: { + Authorization: 'Bearer ' + authtoken, + 'Content-Type': 'application/json' + }, + json: { + type: 'TXT', + name: dnsPrefix, + data: txt, + // Note: set a LOW ttl so that responses are not cached so long + ttl: 300 + } + }).then(function(resp) { + resp = resp.body; + if (resp && resp.domain_record && resp.domain_record.data === txt) { + return true; + } + throw new Error('record did not set. check subdomain, api key, etc'); + }); + }); + }, + remove: function(data) { + var ch = data.challenge; + var zone = ch.identifier.value; + var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); + var txt = ch.dnsAuthorization; + var url = baseUrl + '/v2/domains/' + zone + '/records'; + + // Digital ocean provides the api to remove records by ID. So we first get the recordId and use it to remove the domain record + return helpers.getDomain(domainRecord).then(function(zone) { + var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); + + var payload = { + dnsPrefix: dnsPrefix, + zone: zone, + txt: data.dnsAuthorization + }; + return helpers.getTXTRecord(payload).then(function(txtRecord) { + if (txtRecord) { + var url = + baseUrl + '/v2/domains/' + zone + '/records/' + txtRecord.id; + + return request({ + method: 'DELETE', + url: url, + headers: { + Authorization: 'Bearer ' + authtoken, + 'Content-Type': 'application/json' + } + }).then(function(resp) { + resp = resp.body; + console.log(resp); + return true; + }); + } else { + throw new Error('Txt Record not found for removal'); + } + }); + }); + }, + get: function(data) { + var ch = data.challenge; + var domainRecord = data.challenge.altname; + console.log('DATA:xx', data); + + return helpers.getDomain(domainRecord).then(function(zone) { + var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); + + console.info('Fetching TXT', data); + var payload = { + dnsPrefix: dnsPrefix, + zone: zone, + txt: data.dnsAuthorization + }; + return helpers.getTXTRecord(payload).then(function(txtRecord) { + if (txtRecord) { + return { dnsAuthorization: entry.data }; + } else { + return null; + } + }); }); } }; diff --git a/package-lock.json b/package-lock.json index def9282..827d311 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw==" }, "acme-challenge-test": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acme-challenge-test/-/acme-challenge-test-3.1.0.tgz", - "integrity": "sha512-k+2hNED8P245BdJh44+eJjmVr7oNcuxigMbfz2/7Emc5h4ZAI7iuRZ2M6t4Qui+q5YgiPPjnILDzCZTHLzb1Ag==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/acme-challenge-test/-/acme-challenge-test-3.1.2.tgz", + "integrity": "sha512-/FLQ35C96sexz3qLzEOAAImsnj/5PRND4DieduFvp6E1rKWTxmKMSPPP7gNmZqoU58MbGrpwYieYzB9KQiqMUw==", "dev": true } } diff --git a/package.json b/package.json index b66f221..e182110 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,6 @@ "@root/request": "^1.3.11" }, "devDependencies": { - "acme-challenge-test": "^3.1.0" + "acme-challenge-test": "^3.1.2" } }