acme-dns-01-ovh.js/lib/index.js

197 рядки
4.9 KiB
JavaScript

'use strict';
var request = require('@root/request');
request = require('util').promisify(request);
var crypto = require('crypto');
var querystring = require('querystring');
var defaults = {
region:'ovh-eu',
basePath:'/1.0'
};
var recordsStore = {};
module.exports.create = function(config) {
var applicationKey = config.applicationKey||null;
var applicationSecret = config.applicationSecret||null;
var consumerKey = config.consumerKey||null;
// one of ovh-eu, ovh-ca, kimsufi-eu, kimsufi-ca, soyoustart-eu, soyoustart-ca, runabove-ca
var region = config.region || defaults.region;
var apiTimeDiff = config.apiTimeDiff || null;
var basePath = config.basePath || defaults.basePath;
if (typeof(applicationKey) !== 'string' || typeof(applicationSecret) !== 'string') {
throw new Error('[OVH] You should precise an application key / secret');
}
var endpoints = {
'ovh-eu': 'eu.api.ovh.com',
'ovh-ca': 'ca.api.ovh.com',
'kimsufi-eu': 'eu.api.kimsufi.com',
'kimsufi-ca': 'ca.api.kimsufi.com',
'soyoustart-eu': 'eu.api.soyoustart.com',
'soyoustart-ca': 'ca.api.soyoustart.com',
'runabove-ca': 'api.runabove.com'
};
var baseUrl = 'https://' + endpoints[region] + basePath;
/**
* Signs an API request
*
* @param {String} httpMethod
* @param {String} url
* @param {String} body
* @param {Number|String} timestamp
* @return {String} The signature
*/
function signRequest(httpMethod, url, body, timestamp) {
var s = [
applicationSecret,
consumerKey,
httpMethod,
url,
body || '',
timestamp
];
return '$1$' + crypto.createHash('sha1').update(s.join('+')).digest('hex');
}
function api(httpMethod, path, params) {
// Time drift
if (apiTimeDiff === null && path !== '/auth/time') {
return api('GET', '/auth/time', {})
.then(function(res) {
apiTimeDiff = res.body - Math.round(Date.now() / 1000);
return api(httpMethod, path, params);
}).catch(function(err) {
// todo
throw new Error('[OVH] Unable to fetch OVH API time');
});
}
var headers = {
'X-Ovh-Application': applicationKey
};
if (httpMethod === 'GET') {
path += '?' + querystring.stringify(params);
}
var url = baseUrl + path;
if (path.indexOf('/auth') < 0) {
headers['X-Ovh-Timestamp'] = Math.round(Date.now() / 1000) + apiTimeDiff;
// Sign request
if (typeof(consumerKey) === 'string') {
headers['X-Ovh-Consumer'] = consumerKey;
headers['X-Ovh-Signature'] = signRequest(
httpMethod, url, params, headers['X-Ovh-Timestamp']
);
}
}
console.debug(
'[OVH] API call:',
httpMethod,
path,
params || ''
);
return request({
method: httpMethod,
url: url,
headers: headers,
json: true,
form: params
});
}
return {
init: function(deps) {
request = deps.request;
return null;
},
zones: function(data) {
return api('GET', '/domain/zone')
.then(function(resp) {
if (200 !== resp.statusCode) {
console.error(resp.statusCode);
console.error(resp.body);
throw new Error('Could not get list of zones.');
}
// list of zones
return resp.body;
}).catch(function(err) {
});
},
set: function(data) {
var ch = data.challenge;
var txt = ch.dnsAuthorization;
// record name received as argument : www.qwerty.sampledomain.com
// received zone id : sampledomain.com required record name by OVH : www.qwerty
//console.debug('adding txt', data);
return api('POST', '/domain/zone/' + ch.dnsZone + '/record', {
fieldType: 'TXT',
subDomain: ch.dnsPrefix,
target: txt,
ttl: 1
}).then(function(resp) {
if (200 !== resp.statusCode) {
console.error(resp.statusCode);
console.error(resp.body);
throw new Error('record did not set.');
}
// save id for remove
recordsStore[ch.dnsPrefix] = resp.body['id']
}).then(function() {
// Apply zone modification on DNS servers
return api('POST', '/domain/zone/' + ch.dnsZone + '/record', {})
.then(function() {
return true;
});
});
},
remove: function(data) {
var ch = data.challenge;
return api('DELETE', '/domain/zone/' + ch.dnsZone + '/record/'+recordsStore[ch.dnsPrefix])
.then(function(resp) {
if (200 !== resp.statusCode) {
throw new Error('record did not remove.');
}
})
.then(function() {
return api('POST', '/domain/zone/' + ch.dnsZone + '/record', {})
.then(function(resp) {
if (200 !== resp.statusCode) {
console.error(resp.statusCode);
console.error(resp.body);
throw new Error('record did not remove.');
}
return true;
});
});
},
get: function(data) {
var ch = data.challenge;
return api('GET', '/domain/zone/' + ch.dnsZone + '/record/'+recordsStore[ch.dnsPrefix])
.then(function(resp) {
if (200 !== resp.statusCode) {
throw new Error('record did not remove.');
}
return {
dnsAuthorization:resp.body.target
};
});
}
};
};