196 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			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) {
 | |
| 					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
 | |
| 					};
 | |
| 
 | |
| 			});
 | |
| 		}
 | |
| 	};
 | |
| };
 |