parent
							
								
									a6ef089076
								
							
						
					
					
						commit
						eebb2cb219
					
				
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							@ -13,7 +13,7 @@ npm install --save acme-dns-01-cloudflare@3.x
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Usage
 | 
					# Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
First you create an instance with your credentials:
 | 
					First you create an instance with your account credentials:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
var dns01 = require('acme-dns-01-cloudflare').create({
 | 
					var dns01 = require('acme-dns-01-cloudflare').create({
 | 
				
			||||||
@ -22,6 +22,18 @@ var dns01 = require('acme-dns-01-cloudflare').create({
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					or token credentials:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					var dns01 = require('acme-dns-01-cloudflare').create({
 | 
				
			||||||
 | 
						bearerTokens: {
 | 
				
			||||||
 | 
							list: '123yourListToken', // This token needs to be able to list all of your zones
 | 
				
			||||||
 | 
							zone: '456yourZoneToken' // This token needs to have full control over the targeted DNS zone(s)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						authEmail: 'you@example.com'
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Then you can use it with any compatible ACME module,
 | 
					Then you can use it with any compatible ACME module,
 | 
				
			||||||
such as Greenlock.js or ACME.js.
 | 
					such as Greenlock.js or ACME.js.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -72,6 +84,8 @@ for more implementation details.
 | 
				
			|||||||
# Tests
 | 
					# Tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
# node ./test.js domain-zone auth-key auth-email
 | 
					# node ./test.js domain-zone auth-email auth-type auth-credential (aux-credential?)
 | 
				
			||||||
node ./test.js example.com xxxxxx you@example.com
 | 
					node ./test.js example.com you@example.com key YourApiKey
 | 
				
			||||||
 | 
					node ./test.js example.com you@example.com token YourApiTokenWithFullRights
 | 
				
			||||||
 | 
					node ./test.js example.com you@example.com token YourApiTokenWithListRights YourApiTokenWithEditRightsForTheZone
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										56
									
								
								lib/index.js
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								lib/index.js
									
									
									
									
									
								
							@ -6,10 +6,16 @@ request = require('util').promisify(request)
 | 
				
			|||||||
const Joi = require('@hapi/joi')
 | 
					const Joi = require('@hapi/joi')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema = Joi.object().keys({
 | 
					const schema = Joi.object().keys({
 | 
				
			||||||
  authKey: Joi.string().alphanum().required(),
 | 
					  authKey: Joi.string().alphanum(),
 | 
				
			||||||
 | 
					  bearerTokens: Joi.object({
 | 
				
			||||||
 | 
					    list: Joi.string().alphanum().required(),
 | 
				
			||||||
 | 
					    zone: Joi.string().alphanum().required()
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
  authEmail: Joi.string().email({ minDomainSegments: 2 }).required(),
 | 
					  authEmail: Joi.string().email({ minDomainSegments: 2 }).required(),
 | 
				
			||||||
  baseUrl: Joi.string()
 | 
					  baseUrl: Joi.string()
 | 
				
			||||||
}).with('username', 'birthyear').without('password', 'access_token')
 | 
					}).with('username', 'birthyear').without('password', 'access_token').nand(
 | 
				
			||||||
 | 
					  'authKey', 'bearerTokens'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function formatError (msg, resp) {
 | 
					function formatError (msg, resp) {
 | 
				
			||||||
  const e = new Error(`${resp.statusCode}: ${msg}! (Check the Credentials)`)
 | 
					  const e = new Error(`${resp.statusCode}: ${msg}! (Check the Credentials)`)
 | 
				
			||||||
@ -27,25 +33,35 @@ var defaults = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.create = function (config) {
 | 
					module.exports.create = function (config) {
 | 
				
			||||||
  const baseUrl = (config.baseUrl || defaults.baseUrl).replace(/\/$/, '')
 | 
					 | 
				
			||||||
  Joi.validate(config, schema)
 | 
					  Joi.validate(config, schema)
 | 
				
			||||||
 | 
					  const baseUrl = (config.baseUrl || defaults.baseUrl).replace(/\/$/, '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function api (method, path, body) {
 | 
					  const defaultHeaders = {
 | 
				
			||||||
    return request({
 | 
					 | 
				
			||||||
      method: method,
 | 
					 | 
				
			||||||
      url: baseUrl + path,
 | 
					 | 
				
			||||||
      headers: {
 | 
					 | 
				
			||||||
    'Content-Type': 'application/json',
 | 
					    'Content-Type': 'application/json',
 | 
				
			||||||
        'X-Auth-Key': config.authKey,
 | 
					 | 
				
			||||||
    'X-Auth-Email': config.authEmail
 | 
					    'X-Auth-Email': config.authEmail
 | 
				
			||||||
      },
 | 
					  }
 | 
				
			||||||
 | 
					  if (config.authKey) {
 | 
				
			||||||
 | 
					    defaultHeaders['X-Auth-Key'] = config.authKey
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  function api (method, path, body, tokenType) {
 | 
				
			||||||
 | 
					    const headers = defaultHeaders;
 | 
				
			||||||
 | 
					    if (tokenType && config.bearerTokens) {
 | 
				
			||||||
 | 
					      if (!(tokenType in config.bearerTokens)) {
 | 
				
			||||||
 | 
					        throw new Error('Unrecognized token type');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      headers['Authorization'] = 'Bearer ' + config.bearerTokens[tokenType];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return request({
 | 
				
			||||||
 | 
					      url: baseUrl + path,
 | 
				
			||||||
      json: true,
 | 
					      json: true,
 | 
				
			||||||
 | 
					      method,
 | 
				
			||||||
 | 
					      headers,
 | 
				
			||||||
      body
 | 
					      body
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function zones (domain) {
 | 
					  async function zones (domain) {
 | 
				
			||||||
    const resp = await api('GET', '/zones?per_page=1000' + (domain ? '&name=' + domain : '')) // TODO: use proper pagination?!
 | 
					    const resp = await api('GET', '/zones?per_page=1000' + (domain ? '&name=' + domain : ''), undefined, 'list') // TODO: use proper pagination?!
 | 
				
			||||||
    if (resp.statusCode !== 200) {
 | 
					    if (resp.statusCode !== 200) {
 | 
				
			||||||
      formatError('Could not get list of Zones', resp)
 | 
					      formatError('Could not get list of Zones', resp)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -70,14 +86,13 @@ module.exports.create = function (config) {
 | 
				
			|||||||
          dnsPrefix
 | 
					          dnsPrefix
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } = data
 | 
					      } = data
 | 
				
			||||||
      console.log(data)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const zone = await getZone(domain)
 | 
					      const zone = await getZone(domain)
 | 
				
			||||||
      if (zone.permissions.indexOf('#zone:edit') === -1) {
 | 
					      if (zone.permissions.indexOf('#zone:edit') === -1) {
 | 
				
			||||||
        throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account')
 | 
					        throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resp = await api('POST', `/zones/${zone.id}/dns_records`, {type: 'TXT', name: dnsPrefix, content: txtRecord, ttl: 300})
 | 
					      const resp = await api('POST', `/zones/${zone.id}/dns_records`, {type: 'TXT', name: dnsPrefix, content: txtRecord, ttl: 300}, 'zone')
 | 
				
			||||||
      if (resp.statusCode !== 200) {
 | 
					      if (resp.statusCode !== 200) {
 | 
				
			||||||
        formatError('Could not add record', resp)
 | 
					        formatError('Could not add record', resp)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -88,6 +103,7 @@ module.exports.create = function (config) {
 | 
				
			|||||||
      const {
 | 
					      const {
 | 
				
			||||||
        challenge: {
 | 
					        challenge: {
 | 
				
			||||||
          dnsZone: domain,
 | 
					          dnsZone: domain,
 | 
				
			||||||
 | 
					          dnsAuthorization: txtRecord,
 | 
				
			||||||
          dnsPrefix
 | 
					          dnsPrefix
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } = data
 | 
					      } = data
 | 
				
			||||||
@ -97,28 +113,28 @@ module.exports.create = function (config) {
 | 
				
			|||||||
        throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account')
 | 
					        throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`)
 | 
					      const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`, undefined,'zone')
 | 
				
			||||||
      if (resp.statusCode !== 200) {
 | 
					      if (resp.statusCode !== 200) {
 | 
				
			||||||
        formatError('Could not read record', resp)
 | 
					        formatError('Could not read record', resp)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let {result} = resp.body
 | 
					      let {result} = resp.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let record = result.filter(record => (record.type === 'TXT'))[0]
 | 
					      let record = result.filter(record => (record.type === 'TXT' && record.content === txtRecord))[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (record) {
 | 
					      if (record) {
 | 
				
			||||||
        const resp = await api('DELETE', `/zones/${zone.id}/dns_records/${record.id}`)
 | 
					        const resp = await api('DELETE', `/zones/${zone.id}/dns_records/${record.id}`, undefined, 'zone')
 | 
				
			||||||
        if (resp.statusCode !== 200) {
 | 
					        if (resp.statusCode !== 200) {
 | 
				
			||||||
          formatError('Could not delete record', resp)
 | 
					          formatError('Could not delete record', resp)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        return null // TODO: not found. should this throw?!
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      return null // TODO: not found. should this throw?!
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    get: async function (data) {
 | 
					    get: async function (data) {
 | 
				
			||||||
      const {
 | 
					      const {
 | 
				
			||||||
        challenge: {
 | 
					        challenge: {
 | 
				
			||||||
          dnsZone: domain,
 | 
					          dnsZone: domain,
 | 
				
			||||||
 | 
					          dnsAuthorization: txtRecord,
 | 
				
			||||||
          dnsPrefix
 | 
					          dnsPrefix
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } = data
 | 
					      } = data
 | 
				
			||||||
@ -128,14 +144,14 @@ module.exports.create = function (config) {
 | 
				
			|||||||
        throw new Error('Can not read zone ' + JSON.stringify(domain) + ' from this account')
 | 
					        throw new Error('Can not read zone ' + JSON.stringify(domain) + ' from this account')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`)
 | 
					      const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`, undefined, 'zone')
 | 
				
			||||||
      if (resp.statusCode !== 200) {
 | 
					      if (resp.statusCode !== 200) {
 | 
				
			||||||
        formatError('Could not read record', resp)
 | 
					        formatError('Could not read record', resp)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let {result} = resp.body
 | 
					      let {result} = resp.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let record = result.filter(record => (record.type === 'TXT'))[0]
 | 
					      let record = result.filter(record => (record.type === 'TXT' && record.content === txtRecord))[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (record) {
 | 
					      if (record) {
 | 
				
			||||||
        return {dnsAuthorization: record.content}
 | 
					        return {dnsAuthorization: record.content}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								test.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								test.js
									
									
									
									
									
								
							@ -2,11 +2,21 @@
 | 
				
			|||||||
'use strict'
 | 
					'use strict'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://git.rootprojects.org/root/acme-dns-01-test.js
 | 
					// https://git.rootprojects.org/root/acme-dns-01-test.js
 | 
				
			||||||
var tester = require('acme-dns-01-test')
 | 
					const tester = require('acme-dns-01-test')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Usage: node ./test.js example.com xxxxxxxxx
 | 
					// Usage: node ./test.js example.com you@example.com key xxxxxxxxx
 | 
				
			||||||
let [zone, authKey, authEmail] = process.argv.slice(2)
 | 
					// Usage: node ./test.js example.com you@example.com token xxxxxxxxx
 | 
				
			||||||
var challenger = require('./index.js').create({ authKey, authEmail })
 | 
					// Usage: node ./test.js example.com you@example.com token xxxxxxxxx yyyyyyy
 | 
				
			||||||
 | 
					const [zone, authEmail, authType, credential, zoneToken] = process.argv.slice(2)
 | 
				
			||||||
 | 
					const config = { authEmail }
 | 
				
			||||||
 | 
					switch (authType) {
 | 
				
			||||||
 | 
					  case 'token':
 | 
				
			||||||
 | 
					    config.bearerTokens = {list: credential, zone: zoneToken || credential}
 | 
				
			||||||
 | 
					    break
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    config.authKey = credential
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const challenger = require('./index.js').create(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The dry-run tests can pass on, literally, 'example.com'
 | 
					// The dry-run tests can pass on, literally, 'example.com'
 | 
				
			||||||
// but the integration tests require that you have control over the domain
 | 
					// but the integration tests require that you have control over the domain
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user