2018-05-06 18:57:27 -07:00
'use strict' ;
exports = module . exports = {
2021-08-13 17:22:28 -07:00
removePrivateFields ,
injectPrivateFields ,
upsert ,
get ,
del ,
wait ,
2022-01-05 22:41:41 -08:00
verifyDomainConfig
2018-05-06 18:57:27 -07:00
} ;
2021-08-13 17:22:28 -07:00
const assert = require ( 'assert' ) ,
2019-10-23 10:02:04 -07:00
BoxError = require ( '../boxerror.js' ) ,
2020-05-14 23:01:44 +02:00
constants = require ( '../constants.js' ) ,
2018-05-06 18:57:27 -07:00
debug = require ( 'debug' ) ( 'box:dns/gandi' ) ,
2021-08-13 17:22:28 -07:00
dns = require ( '../dns.js' ) ,
2018-05-06 18:57:27 -07:00
superagent = require ( 'superagent' ) ,
2019-01-04 18:44:54 -08:00
util = require ( 'util' ) ,
waitForDns = require ( './waitfordns.js' ) ;
2018-05-06 18:57:27 -07:00
2021-08-13 17:22:28 -07:00
const GANDI _API = 'https://dns.api.gandi.net/api/v5' ;
2018-05-06 18:57:27 -07:00
function formatError ( response ) {
2018-05-06 20:58:11 -07:00
return util . format ( ` Gandi DNS error [ ${ response . statusCode } ] ${ response . body . message } ` ) ;
2018-05-06 18:57:27 -07:00
}
2019-02-08 11:11:49 +01:00
function removePrivateFields ( domainObject ) {
2020-05-14 23:01:44 +02:00
domainObject . config . token = constants . SECRET _PLACEHOLDER ;
2019-02-08 11:11:49 +01:00
return domainObject ;
}
2019-02-09 19:08:15 +01:00
function injectPrivateFields ( newConfig , currentConfig ) {
2020-05-14 23:01:44 +02:00
if ( newConfig . token === constants . SECRET _PLACEHOLDER ) newConfig . token = currentConfig . token ;
2019-02-09 19:08:15 +01:00
}
2019-01-04 18:44:54 -08:00
function upsert ( domainObject , location , type , values , callback ) {
assert . strictEqual ( typeof domainObject , 'object' ) ;
assert . strictEqual ( typeof location , 'string' ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof type , 'string' ) ;
2021-05-02 11:26:08 -07:00
assert ( Array . isArray ( values ) ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2022-01-05 22:41:41 -08:00
const domainConfig = domainObject . config ,
2019-01-04 18:44:54 -08:00
zoneName = domainObject . zoneName ,
2021-08-13 17:22:28 -07:00
name = dns . getName ( domainObject , location , type ) || '@' ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
debug ( ` upsert: ${ name } in zone ${ zoneName } of type ${ type } with values ${ JSON . stringify ( values ) } ` ) ;
2018-05-06 18:57:27 -07:00
var data = {
'rrset_ttl' : 300 , // this is the minimum allowed
2018-05-06 22:14:39 -07:00
'rrset_values' : values // for mx records, value is already of the '<priority> <server>' format
2018-05-06 18:57:27 -07:00
} ;
2019-01-04 18:44:54 -08:00
superagent . put ( ` ${ GANDI _API } /domains/ ${ zoneName } /records/ ${ name } / ${ type } ` )
2022-01-05 22:41:41 -08:00
. set ( 'X-Api-Key' , domainConfig . token )
2018-05-06 18:57:27 -07:00
. timeout ( 30 * 1000 )
. send ( data )
. end ( function ( error , result ) {
2019-10-23 10:02:04 -07:00
if ( error && ! error . response ) return callback ( new BoxError ( BoxError . NETWORK _ERROR , error . message ) ) ;
if ( result . statusCode === 403 || result . statusCode === 401 ) return callback ( new BoxError ( BoxError . ACCESS _DENIED , formatError ( result ) ) ) ;
if ( result . statusCode === 400 ) return callback ( new BoxError ( BoxError . BAD _FIELD , formatError ( result ) ) ) ;
if ( result . statusCode !== 201 ) return callback ( new BoxError ( BoxError . EXTERNAL _ERROR , formatError ( result ) ) ) ;
2018-05-06 18:57:27 -07:00
2018-06-29 22:25:34 +02:00
return callback ( null ) ;
2018-05-06 18:57:27 -07:00
} ) ;
}
2019-01-04 18:44:54 -08:00
function get ( domainObject , location , type , callback ) {
assert . strictEqual ( typeof domainObject , 'object' ) ;
assert . strictEqual ( typeof location , 'string' ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof type , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2022-01-05 22:41:41 -08:00
const domainConfig = domainObject . config ,
2019-01-04 18:44:54 -08:00
zoneName = domainObject . zoneName ,
2021-08-13 17:22:28 -07:00
name = dns . getName ( domainObject , location , type ) || '@' ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
debug ( ` get: ${ name } in zone ${ zoneName } of type ${ type } ` ) ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
superagent . get ( ` ${ GANDI _API } /domains/ ${ zoneName } /records/ ${ name } / ${ type } ` )
2022-01-05 22:41:41 -08:00
. set ( 'X-Api-Key' , domainConfig . token )
2018-05-06 18:57:27 -07:00
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
2019-10-23 10:02:04 -07:00
if ( error && ! error . response ) return callback ( new BoxError ( BoxError . NETWORK _ERROR , error . message ) ) ;
if ( result . statusCode === 403 || result . statusCode === 401 ) return callback ( new BoxError ( BoxError . ACCESS _DENIED , formatError ( result ) ) ) ;
2018-05-06 20:58:11 -07:00
if ( result . statusCode === 404 ) return callback ( null , [ ] ) ;
2019-10-23 10:02:04 -07:00
if ( result . statusCode !== 200 ) return callback ( new BoxError ( BoxError . EXTERNAL _ERROR , formatError ( result ) ) ) ;
2018-05-06 18:57:27 -07:00
debug ( 'get: %j' , result . body ) ;
return callback ( null , result . body . rrset _values ) ;
} ) ;
}
2019-01-04 18:44:54 -08:00
function del ( domainObject , location , type , values , callback ) {
assert . strictEqual ( typeof domainObject , 'object' ) ;
assert . strictEqual ( typeof location , 'string' ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof type , 'string' ) ;
2021-05-02 11:26:08 -07:00
assert ( Array . isArray ( values ) ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2022-01-05 22:41:41 -08:00
const domainConfig = domainObject . config ,
2019-01-04 18:44:54 -08:00
zoneName = domainObject . zoneName ,
2021-08-13 17:22:28 -07:00
name = dns . getName ( domainObject , location , type ) || '@' ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
debug ( ` del: ${ name } in zone ${ zoneName } of type ${ type } with values ${ JSON . stringify ( values ) } ` ) ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
superagent . del ( ` ${ GANDI _API } /domains/ ${ zoneName } /records/ ${ name } / ${ type } ` )
2022-01-05 22:41:41 -08:00
. set ( 'X-Api-Key' , domainConfig . token )
2018-05-06 18:57:27 -07:00
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
2019-10-23 10:02:04 -07:00
if ( error && ! error . response ) return callback ( new BoxError ( BoxError . NETWORK _ERROR , error . message ) ) ;
2018-05-06 18:57:27 -07:00
if ( result . statusCode === 404 ) return callback ( null ) ;
2019-10-23 10:02:04 -07:00
if ( result . statusCode === 403 || result . statusCode === 401 ) return callback ( new BoxError ( BoxError . ACCESS _DENIED , formatError ( result ) ) ) ;
if ( result . statusCode !== 204 ) return callback ( new BoxError ( BoxError . EXTERNAL _ERROR , formatError ( result ) ) ) ;
2018-05-06 18:57:27 -07:00
debug ( 'del: done' ) ;
return callback ( null ) ;
} ) ;
}
2022-02-03 16:15:14 -08:00
async function wait ( domainObject , subdomain , type , value , options ) {
2019-01-04 18:44:54 -08:00
assert . strictEqual ( typeof domainObject , 'object' ) ;
2022-02-03 16:15:14 -08:00
assert . strictEqual ( typeof subdomain , 'string' ) ;
2019-01-04 18:44:54 -08:00
assert . strictEqual ( typeof type , 'string' ) ;
assert . strictEqual ( typeof value , 'string' ) ;
assert ( options && typeof options === 'object' ) ; // { interval: 5000, times: 50000 }
2022-02-03 16:15:14 -08:00
const fqdn = dns . fqdn ( subdomain , domainObject ) ;
2019-01-04 18:44:54 -08:00
2022-02-03 16:15:14 -08:00
await waitForDns ( fqdn , domainObject . zoneName , type , value , options ) ;
2019-01-04 18:44:54 -08:00
}
2022-01-05 22:41:41 -08:00
function verifyDomainConfig ( domainObject , callback ) {
2019-01-04 18:44:54 -08:00
assert . strictEqual ( typeof domainObject , 'object' ) ;
2018-05-06 18:57:27 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2022-01-05 22:41:41 -08:00
const domainConfig = domainObject . config ,
2019-01-04 18:44:54 -08:00
zoneName = domainObject . zoneName ;
2022-01-05 22:41:41 -08:00
if ( ! domainConfig . token || typeof domainConfig . token !== 'string' ) return callback ( new BoxError ( BoxError . BAD _FIELD , 'token must be a non-empty string' , { field : 'token' } ) ) ;
2018-06-17 21:44:08 -07:00
2018-05-06 18:57:27 -07:00
var credentials = {
2022-01-05 22:41:41 -08:00
token : domainConfig . token
2018-05-06 18:57:27 -07:00
} ;
2019-01-04 18:44:54 -08:00
const ip = '127.0.0.1' ;
2018-05-06 18:57:27 -07:00
if ( process . env . BOX _ENV === 'test' ) return callback ( null , credentials ) ; // this shouldn't be here
dns . resolve ( zoneName , 'NS' , { timeout : 5000 } , function ( error , nameservers ) {
2019-10-23 10:02:04 -07:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( new BoxError ( BoxError . BAD _FIELD , 'Unable to resolve nameservers for this domain' , { field : 'nameservers' } ) ) ;
if ( error || ! nameservers ) return callback ( new BoxError ( BoxError . BAD _FIELD , error ? error . message : 'Unable to get nameservers' , { field : 'nameservers' } ) ) ;
2018-05-06 18:57:27 -07:00
if ( ! nameservers . every ( function ( n ) { return n . toLowerCase ( ) . indexOf ( '.gandi.net' ) !== - 1 ; } ) ) {
2022-01-05 22:41:41 -08:00
debug ( 'verifyDomainConfig: %j does not contain Gandi NS' , nameservers ) ;
2019-10-23 10:02:04 -07:00
return callback ( new BoxError ( BoxError . BAD _FIELD , 'Domain nameservers are not set to Gandi' , { field : 'nameservers' } ) ) ;
2018-05-06 18:57:27 -07:00
}
2019-01-04 18:44:54 -08:00
const location = 'cloudrontestdns' ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
upsert ( domainObject , location , 'A' , [ ip ] , function ( error ) {
2018-05-06 18:57:27 -07:00
if ( error ) return callback ( error ) ;
2022-01-05 22:41:41 -08:00
debug ( 'verifyDomainConfig: Test A record added' ) ;
2018-05-06 18:57:27 -07:00
2019-01-04 18:44:54 -08:00
del ( domainObject , location , 'A' , [ ip ] , function ( error ) {
2018-05-06 18:57:27 -07:00
if ( error ) return callback ( error ) ;
2022-01-05 22:41:41 -08:00
debug ( 'verifyDomainConfig: Test A record removed again' ) ;
2018-05-06 18:57:27 -07:00
callback ( null , credentials ) ;
} ) ;
} ) ;
} ) ;
}