Files
cloudron-box/src/test/dns-providers-test.js
2022-04-28 17:46:03 -07:00

1112 lines
44 KiB
JavaScript

/* jslint node:true */
/* global it:false */
/* global describe:false */
/* global before:false */
/* global beforeEach:false */
/* global after:false */
'use strict';
const AWS = require('aws-sdk'),
common = require('./common.js'),
dns = require('../dns.js'),
domains = require('../domains.js'),
expect = require('expect.js'),
GCDNS = require('@google-cloud/dns').DNS,
nock = require('nock'),
_ = require('underscore');
describe('dns provider', function () {
const { setup, cleanup, auditSource, domain } = common;
const domainCopy = Object.assign({}, domain); // make a copy
before(setup);
after(cleanup);
describe('noop', function () {
before(async function () {
domainCopy.provider = 'noop';
domainCopy.config = {};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
it('upsert succeeds', async function () {
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
});
it('get succeeds', async function () {
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result.length).to.eql(0);
});
it('del succeeds', async function () {
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
});
});
describe('digitalocean', function () {
const TOKEN = 'sometoken';
const DIGITALOCEAN_ENDPOINT = 'https://api.digitalocean.com';
before(async function () {
domainCopy.provider = 'digitalocean';
domainCopy.config = {
token: TOKEN
};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
it('upsert non-existing record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 3352892,
type: 'A',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const req1 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.get('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(200, { domain_records: [] });
const req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.post('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(201, { domain_record: DOMAIN_RECORD_0 });
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('upsert existing record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 3352892,
type: 'A',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1 = {
id: 3352893,
type: 'A',
name: 'test',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1_NEW = {
id: 3352893,
type: 'A',
name: 'test',
data: '1.2.3.5',
priority: null,
port: null,
weight: null
};
const req1 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.get('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(200, { domain_records: [DOMAIN_RECORD_0, DOMAIN_RECORD_1] });
const req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.put('/v2/domains/' + domainCopy.zoneName + '/records/' + DOMAIN_RECORD_1.id)
.reply(200, { domain_record: DOMAIN_RECORD_1_NEW });
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', [DOMAIN_RECORD_1_NEW.data]);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('upsert multiple record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 3352892,
type: 'A',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1 = {
id: 3352893,
type: 'TXT',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1_NEW = {
id: 3352893,
type: 'TXT',
name: '@',
data: 'somethingnew',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_2 = {
id: 3352894,
type: 'TXT',
name: '@',
data: 'something',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_2_NEW = {
id: 3352894,
type: 'TXT',
name: '@',
data: 'somethingnew',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_3_NEW = {
id: 3352895,
type: 'TXT',
name: '@',
data: 'thirdnewone',
priority: null,
port: null,
weight: null
};
const req1 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.get('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(200, { domain_records: [DOMAIN_RECORD_0, DOMAIN_RECORD_1, DOMAIN_RECORD_2] });
const req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.put('/v2/domains/' + domainCopy.zoneName + '/records/' + DOMAIN_RECORD_1.id)
.reply(200, { domain_record: DOMAIN_RECORD_1_NEW });
const req3 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.put('/v2/domains/' + domainCopy.zoneName + '/records/' + DOMAIN_RECORD_2.id)
.reply(200, { domain_record: DOMAIN_RECORD_2_NEW });
const req4 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.post('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(201, { domain_record: DOMAIN_RECORD_2_NEW });
await dns.upsertDnsRecords('', domainCopy.domain, 'TXT', [DOMAIN_RECORD_2_NEW.data, DOMAIN_RECORD_1_NEW.data, DOMAIN_RECORD_3_NEW.data]);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
expect(req3.isDone()).to.be.ok();
expect(req4.isDone()).to.be.ok();
});
it('get succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 3352892,
type: 'A',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1 = {
id: 3352893,
type: 'A',
name: 'test',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const req1 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.get('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(200, { domain_records: [DOMAIN_RECORD_0, DOMAIN_RECORD_1] });
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(1);
expect(result[0]).to.eql(DOMAIN_RECORD_1.data);
expect(req1.isDone()).to.be.ok();
});
it('del succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 3352892,
type: 'A',
name: '@',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const DOMAIN_RECORD_1 = {
id: 3352893,
type: 'A',
name: 'test',
data: '1.2.3.4',
priority: null,
port: null,
weight: null
};
const req1 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.get('/v2/domains/' + domainCopy.zoneName + '/records')
.reply(200, { domain_records: [DOMAIN_RECORD_0, DOMAIN_RECORD_1] });
const req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; })
.delete('/v2/domains/' + domainCopy.zoneName + '/records/' + DOMAIN_RECORD_1.id)
.reply(204, {});
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
});
describe('godaddy', function () {
const KEY = 'somekey', SECRET = 'somesecret';
const GODADDY_API = 'https://api.godaddy.com/v1/domains';
before(async function () {
domainCopy.provider = 'godaddy';
domainCopy.config = {
apiKey: KEY,
apiSecret: SECRET
};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
it('upsert record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = [{
ttl: 600,
data: '1.2.3.4'
}];
const req1 = nock(GODADDY_API)
.put('/' + domainCopy.zoneName + '/records/A/test', DOMAIN_RECORD_0)
.reply(200, {});
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
});
it('get succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = [{
ttl: 600,
data: '1.2.3.4'
}];
const req1 = nock(GODADDY_API)
.get('/' + domainCopy.zoneName + '/records/A/test')
.reply(200, DOMAIN_RECORD_0);
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(1);
expect(result[0]).to.eql(DOMAIN_RECORD_0[0].data);
expect(req1.isDone()).to.be.ok();
});
it('del succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = [{ // existing
ttl: 600,
data: '1.2.3.4'
}];
const req1 = nock(GODADDY_API)
.get('/' + domainCopy.zoneName + '/records/A/test')
.reply(200, DOMAIN_RECORD_0);
const req2 = nock(GODADDY_API)
.delete('/' + domainCopy.zoneName + '/records/A/test')
.reply(204, {});
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
});
describe('gandi', function () {
const TOKEN = 'sometoken';
const GANDI_API = 'https://dns.api.gandi.net/api/v5';
before(async function () {
domainCopy.provider = 'gandi';
domainCopy.config = {
token: TOKEN
};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
it('upsert record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
'rrset_ttl': 300,
'rrset_values': ['1.2.3.4']
};
const req1 = nock(GANDI_API)
.put('/domains/' + domainCopy.zoneName + '/records/test/A', DOMAIN_RECORD_0)
.reply(201, { message: 'Zone Record Created' });
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
});
it('get succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
'rrset_type': 'A',
'rrset_ttl': 600,
'rrset_name': 'test',
'rrset_values': ['1.2.3.4']
};
const req1 = nock(GANDI_API)
.get('/domains/' + domainCopy.zoneName + '/records/test/A')
.reply(200, DOMAIN_RECORD_0);
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(1);
expect(result[0]).to.eql(DOMAIN_RECORD_0.rrset_values[0]);
expect(req1.isDone()).to.be.ok();
});
it('del succeeds', async function () {
nock.cleanAll();
const req2 = nock(GANDI_API)
.delete('/domains/' + domainCopy.zoneName + '/records/test/A')
.reply(204, {});
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req2.isDone()).to.be.ok();
});
});
describe('name.com', function () {
const TOKEN = 'sometoken';
const NAMECOM_API = 'https://api.name.com/v4';
before(async function () {
domainCopy.provider = 'namecom';
domainCopy.config = {
username: 'fake',
token: TOKEN
};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
it('upsert record succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
host: 'test',
type: 'A',
answer: '1.2.3.4',
ttl: 300
};
const req1 = nock(NAMECOM_API)
.get(`/domains/${domainCopy.zoneName}/records`)
.reply(200, { records: [] });
const req2 = nock(NAMECOM_API)
.post(`/domains/${domainCopy.zoneName}/records`, DOMAIN_RECORD_0)
.reply(200, {});
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('get succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
host: 'test',
type: 'A',
answer: '1.2.3.4',
ttl: 300
};
const req1 = nock(NAMECOM_API)
.get(`/domains/${domainCopy.zoneName}/records`)
.reply(200, { records: [DOMAIN_RECORD_0] });
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(1);
expect(result[0]).to.eql(DOMAIN_RECORD_0.answer);
expect(req1.isDone()).to.be.ok();
});
it('del succeeds', async function () {
nock.cleanAll();
const DOMAIN_RECORD_0 = {
id: 'someid',
host: 'test',
type: 'A',
answer: '1.2.3.4',
ttl: 300
};
const req1 = nock(NAMECOM_API)
.get(`/domains/${domainCopy.zoneName}/records`)
.reply(200, { records: [DOMAIN_RECORD_0] });
const req2 = nock(NAMECOM_API)
.delete(`/domains/${domainCopy.zoneName}/records/${DOMAIN_RECORD_0.id}`)
.reply(200, {});
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
});
describe('namecheap', function () {
const NAMECHEAP_ENDPOINT = 'https://api.namecheap.com';
const username = 'namecheapuser';
const token = 'namecheaptoken';
// the success answer is always the same
const SET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.sethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.setHosts">
<DomainDNSSetHostsResult Domain="cloudron.space" IsSuccess="true">
<Warnings />
</DomainDNSSetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT03</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.408</ExecutionTime>
</ApiResponse>`;
before(async function () {
domainCopy.provider = 'namecheap';
domainCopy.config = {
username: username,
token: token
};
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
beforeEach(function () {
nock.cleanAll();
});
it('upsert non-existing record succeeds', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="@" Type="TXT" Address="v=spf1 a:my.nebulon.space ~all" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
const req2 = nock(NAMECHEAP_ENDPOINT).post('/xml.response', (body) => {
const expected = {
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.setHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1],
TTL1: '300',
HostName1: '@',
RecordType1: 'MX',
Address1: 'my.nebulon.space.',
EmailType1: 'MX',
MXPref1: '10',
TTL2: '300',
HostName2: '@',
RecordType2: 'TXT',
Address2: 'v=spf1 a:my.nebulon.space ~all',
TTL3: '300',
HostName3: 'test',
RecordType3: 'A',
Address3: '1.2.3.4',
};
return _.isEqual(body, expected);
})
.reply(200, SET_HOSTS_RETURN);
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('upsert multiple non-existing records succeeds', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="@" Type="TXT" Address="v=spf1 a:my.nebulon.space ~all" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
const req2 = nock(NAMECHEAP_ENDPOINT).post('/xml.response', (body) => {
const expected = {
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.setHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1],
TTL1: '300',
HostName1: '@',
RecordType1: 'MX',
Address1: 'my.nebulon.space.',
EmailType1: 'MX',
MXPref1: '10',
TTL2: '300',
HostName2: '@',
RecordType2: 'TXT',
Address2: 'v=spf1 a:my.nebulon.space ~all',
TTL3: '300',
HostName3: 'test',
RecordType3: 'TXT',
Address3: '1.2.3.4',
TTL4: '300',
HostName4: 'test',
RecordType4: 'TXT',
Address4: '2.3.4.5',
TTL5: '300',
HostName5: 'test',
RecordType5: 'TXT',
Address5: '3.4.5.6',
};
return _.isEqual(body, expected);
})
.reply(200, SET_HOSTS_RETURN);
await dns.upsertDnsRecords('test', domainCopy.domain, 'TXT', ['1.2.3.4', '2.3.4.5', '3.4.5.6']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('upsert existing record succeeds', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="www" Type="CNAME" Address="1.2.3.4" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
const req2 = nock(NAMECHEAP_ENDPOINT).post('/xml.response', (body) => {
const expected = {
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.setHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1],
TTL1: '300',
HostName1: '@',
RecordType1: 'MX',
Address1: 'my.nebulon.space.',
EmailType1: 'MX',
MXPref1: '10',
TTL2: '300',
HostName2: 'www',
RecordType2: 'CNAME',
Address2: '1.2.3.4'
};
return _.isEqual(body, expected);
})
.reply(200, SET_HOSTS_RETURN);
await dns.upsertDnsRecords('www', domainCopy.domain, 'CNAME', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('get succeeds', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="test" Type="A" Address="1.2.3.4" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400613" Name="test" Type="A" Address="2.3.4.5" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(req1.isDone()).to.be.ok();
expect(result).to.be.an(Array);
expect(result.length).to.eql(2);
expect(result).to.eql(['1.2.3.4', '2.3.4.5']);
});
it('del succeeds', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="www" Type="CNAME" Address="1.2.3.4" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
const req2 = nock(NAMECHEAP_ENDPOINT).post('/xml.response', (body) => {
const expected = {
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.setHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1],
TTL1: '300',
HostName1: '@',
RecordType1: 'MX',
Address1: 'my.nebulon.space.',
EmailType1: 'MX',
MXPref1: '10',
};
return _.isEqual(body, expected);
})
.reply(200, SET_HOSTS_RETURN);
await dns.removeDnsRecords('www', domainCopy.domain, 'CNAME', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
expect(req2.isDone()).to.be.ok();
});
it('del succeeds with non-existing domain', async function () {
const GET_HOSTS_RETURN = `<?xml version="1.0" encoding="utf-8"?>
<ApiResponse Status="OK" xmlns="http://api.namecheap.com/xml.response">
<Errors />
<Warnings />
<RequestedCommand>namecheap.domains.dns.gethosts</RequestedCommand>
<CommandResponse Type="namecheap.domains.dns.getHosts">
<DomainDNSGetHostsResult Domain="cloudron.space" EmailType="MX" IsUsingOurDNS="true">
<host HostId="160859434" Name="@" Type="MX" Address="my.nebulon.space." MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
<host HostId="173400612" Name="www" Type="CNAME" Address="1.2.3.4" MXPref="10" TTL="300" AssociatedAppTitle="" FriendlyName="" IsActive="true" IsDDNSEnabled="false" />
</DomainDNSGetHostsResult>
</CommandResponse>
<Server>PHX01APIEXT04</Server>
<GMTTimeDifference>--4:00</GMTTimeDifference>
<ExecutionTime>0.16</ExecutionTime>
</ApiResponse>`;
const req1 = nock(NAMECHEAP_ENDPOINT).get('/xml.response')
.query({
ApiUser: username,
ApiKey: token,
UserName: username,
ClientIp: '127.0.0.1',
Command: 'namecheap.domains.dns.getHosts',
SLD: domainCopy.zoneName.split('.')[0],
TLD: domainCopy.zoneName.split('.')[1]
})
.reply(200, GET_HOSTS_RETURN);
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(req1.isDone()).to.be.ok();
});
});
describe('route53', function () {
// do not clear this with [] but .length = 0 so we don't loose the reference in mockery
const awsAnswerQueue = []; // every element itself is array: [0] is error and [1] is result
let AWS_HOSTED_ZONES = null;
before(async function () {
domainCopy.provider = 'route53';
domainCopy.config = {
accessKeyId: 'unused',
secretAccessKey: 'unused'
};
AWS_HOSTED_ZONES = {
HostedZones: [{
Id: '/hostedzone/Z34G16B38TNZ9L',
Name: domainCopy.zoneName + '.',
CallerReference: '305AFD59-9D73-4502-B020-F4E6F889CB30',
ResourceRecordSetCount: 2,
ChangeInfo: {
Id: '/change/CKRTFJA0ANHXB',
Status: 'INSYNC'
}
}, {
Id: '/hostedzone/Z3OFC3B6E8YTA7',
Name: 'cloudron.us.',
CallerReference: '0B37F2DE-21A4-E678-BA32-3FC8AF0CF635',
Config: {},
ResourceRecordSetCount: 2,
ChangeInfo: {
Id: '/change/C2682N5HXP0BZ5',
Status: 'INSYNC'
}
}],
IsTruncated: false,
MaxItems: '100'
};
function mockery(queue) {
return function (options) {
expect(options).to.be.an(Object);
return {
promise: async function () {
const elem = queue.shift();
if (!Array.isArray(elem)) throw (new Error('Mock answer required'));
if (elem[0]) throw elem[0];
return elem[1];
}
};
};
}
function Route53Mock(cfg) {
expect(cfg).to.eql({
accessKeyId: domainCopy.config.accessKeyId,
secretAccessKey: domainCopy.config.secretAccessKey,
region: 'us-east-1'
});
}
Route53Mock.prototype.getHostedZone = mockery(awsAnswerQueue);
Route53Mock.prototype.getChange = mockery(awsAnswerQueue);
Route53Mock.prototype.changeResourceRecordSets = mockery(awsAnswerQueue);
Route53Mock.prototype.listResourceRecordSets = mockery(awsAnswerQueue);
Route53Mock.prototype.listHostedZonesByName = mockery(awsAnswerQueue);
// override route53 in AWS
// Comment this out and replace the config with real tokens to test against AWS proper
AWS._originalRoute53 = AWS.Route53;
AWS.Route53 = Route53Mock;
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
after(function () {
AWS.Route53 = AWS._originalRoute53;
delete AWS._originalRoute53;
});
it('upsert non-existing record succeeds', async function () {
awsAnswerQueue.push([null, AWS_HOSTED_ZONES]);
awsAnswerQueue.push([null, {
ChangeInfo: {
Id: '/change/C2QLKQIWEI0BZF',
Status: 'PENDING',
SubmittedAt: 'Mon Aug 04 2014 17: 44: 49 GMT - 0700(PDT)'
}
}]);
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(awsAnswerQueue.length).to.eql(0);
});
it('upsert existing record succeeds', async function () {
awsAnswerQueue.push([null, AWS_HOSTED_ZONES]);
awsAnswerQueue.push([null, {
ChangeInfo: {
Id: '/change/C2QLKQIWEI0BZF',
Status: 'PENDING',
SubmittedAt: 'Mon Aug 04 2014 17: 44: 49 GMT - 0700(PDT)'
}
}]);
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(awsAnswerQueue.length).to.eql(0);
});
it('upsert multiple record succeeds', async function () {
awsAnswerQueue.push([null, AWS_HOSTED_ZONES]);
awsAnswerQueue.push([null, {
ChangeInfo: {
Id: '/change/C2QLKQIWEI0BZF',
Status: 'PENDING',
SubmittedAt: 'Mon Aug 04 2014 17: 44: 49 GMT - 0700(PDT)'
}
}]);
await dns.upsertDnsRecords('', domainCopy.domain, 'TXT', ['first', 'second', 'third']);
expect(awsAnswerQueue.length).to.eql(0);
});
it('get succeeds', async function () {
awsAnswerQueue.push([null, AWS_HOSTED_ZONES]);
awsAnswerQueue.push([null, {
ResourceRecordSets: [{
Name: 'test.' + domainCopy.zoneName + '.',
Type: 'A',
ResourceRecords: [{
Value: '1.2.3.4'
}]
}]
}]);
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(1);
expect(result[0]).to.eql('1.2.3.4');
expect(awsAnswerQueue.length).to.eql(0);
});
it('del succeeds', async function () {
awsAnswerQueue.push([null, AWS_HOSTED_ZONES]);
awsAnswerQueue.push([null, {
ChangeInfo: {
Id: '/change/C2QLKQIWEI0BZF',
Status: 'PENDING',
SubmittedAt: 'Mon Aug 04 2014 17: 44: 49 GMT - 0700(PDT)'
}
}]);
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(awsAnswerQueue.length).to.eql(0);
});
});
describe('gcdns', function () {
let HOSTED_ZONES = [];
const zoneQueue = []; // every element itself is array: [0] is error and [1] is result
let _OriginalGCDNS;
before(async function () {
domainCopy.provider = 'gcdns';
domainCopy.config = {
projectId: 'my-dns-proj',
credentials: {
'client_email': '123456789349-compute@developer.gserviceaccount.com',
'private_key': 'privatehushhush'
}
};
function mockery(queue) {
return async function () {
const elem = queue.shift();
if (!Array.isArray(elem)) throw (new Error('Mock answer required'));
if (elem[0]) throw elem[0];
return [elem[1]]; // gcdns uses second element for apiResponse
};
}
function fakeZone(name, ns, recordQueue) {
const zone = new GCDNS().zone(name.replace('.', '-'));
zone.metadata.dnsName = name + '.';
zone.metadata.nameServers = ns || ['8.8.8.8', '8.8.4.4'];
zone.getRecords = mockery(recordQueue || zoneQueue);
zone.createChange = mockery(recordQueue || zoneQueue);
zone.replaceRecords = mockery(recordQueue || zoneQueue);
zone.deleteRecords = mockery(recordQueue || zoneQueue);
return zone;
}
HOSTED_ZONES = [fakeZone(domainCopy.domain), fakeZone('cloudron.us')];
_OriginalGCDNS = GCDNS.prototype.getZones;
GCDNS.prototype.getZones = mockery(zoneQueue);
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
});
after(function () {
GCDNS.prototype.getZones = _OriginalGCDNS;
_OriginalGCDNS = null;
});
it('upsert non-existing record succeeds', async function () {
zoneQueue.push([null, HOSTED_ZONES]); // getZone
zoneQueue.push([null, []]); // getRecords
zoneQueue.push([null, { id: '1' }]);
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(zoneQueue.length).to.eql(0);
});
it('upsert existing record succeeds', async function () {
zoneQueue.push([null, HOSTED_ZONES]);
zoneQueue.push([null, [new GCDNS().zone('test').record('A', { 'name': 'test', data: ['5.6.7.8'], ttl: 1 })]]);
zoneQueue.push([null, { id: '2' }]);
await dns.upsertDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(zoneQueue.length).to.eql(0);
});
it('upsert multiple record succeeds', async function () {
zoneQueue.push([null, HOSTED_ZONES]);
zoneQueue.push([null, []]); // getRecords
zoneQueue.push([null, { id: '3' }]);
await dns.upsertDnsRecords('', domainCopy.domain, 'TXT', ['first', 'second', 'third']);
expect(zoneQueue.length).to.eql(0);
});
it('get succeeds', async function () {
zoneQueue.push([null, HOSTED_ZONES]);
zoneQueue.push([null, [new GCDNS().zone('test').record('A', { 'name': 'test', data: ['1.2.3.4', '5.6.7.8'], ttl: 1 })]]);
const result = await dns.getDnsRecords('test', domainCopy.domain, 'A');
expect(result).to.be.an(Array);
expect(result.length).to.eql(2);
expect(result).to.eql(['1.2.3.4', '5.6.7.8']);
expect(zoneQueue.length).to.eql(0);
});
it('del succeeds', async function () {
zoneQueue.push([null, HOSTED_ZONES]);
zoneQueue.push([null, [new GCDNS().zone('test').record('A', { 'name': 'test', data: ['5.6.7.8'], ttl: 1 })]]);
zoneQueue.push([null, { id: '5' }]);
await dns.removeDnsRecords('test', domainCopy.domain, 'A', ['1.2.3.4']);
expect(zoneQueue.length).to.eql(0);
});
});
});