/* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ 'use strict'; var appdb = require('../appdb.js'), apps = require('../apps.js'), async = require('async'), database = require('../database.js'), domains = require('../domains.js'), expect = require('expect.js'), js2xml = require('js2xmlparser').parse, nock = require('nock'), settings = require('../settings.js'); const DOMAIN_0 = { domain: 'example.com', zoneName: 'example.com', provider: 'route53', config: { accessKeyId: 'accessKeyId', secretAccessKey: 'secretAccessKey', endpoint: 'http://localhost:5353' }, fallbackCertificate: null, tlsConfig: { provider: 'letsencrypt-staging' }, wellKnown: null }; let AUDIT_SOURCE = { ip: '1.2.3.4' }; var MANIFEST = { 'id': 'io.cloudron.test', 'author': 'The Presidents Of the United States Of America', 'title': 'test title', 'description': 'test description', 'tagline': 'test rocks', 'website': 'http://test.cloudron.io', 'contactEmail': 'test@cloudron.io', 'version': '0.1.0', 'manifestVersion': 1, 'dockerImage': 'cloudron/test:25.2.0', 'healthCheckPath': '/', 'httpPort': 7777, 'tcpPorts': { 'ECHO_SERVER_PORT': { 'title': 'Echo Server Port', 'description': 'Echo server', 'containerPort': 7778 } }, 'addons': { 'oauth': { }, 'redis': { }, 'mysql': { }, 'postgresql': { } } }; var APP = { id: 'appid', appStoreId: 'appStoreId', installationState: apps.ISTATE_PENDING_INSTALL, runState: 'running', location: 'applocation', domain: DOMAIN_0.domain, fqdn: DOMAIN_0.domain + '.' + 'applocation', manifest: MANIFEST, containerId: 'someid', portBindings: null, accessRestriction: null, memoryLimit: 0, mailboxDomain: DOMAIN_0.domain, alternateDomains: [], aliasDomains: [] }; describe('Domains', function () { before(function (done) { async.series([ database.initialize, database._clear, settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), appdb.add.bind(null, APP.id, APP.appStoreId, APP.manifest, APP.location, APP.domain, APP.portBindings, APP) ], done); }); after(function (done) { async.series([ database._clear, database.uninitialize ], done); }); describe('validateHostname', function () { const domain = { domain: 'example.com', zoneName: 'example.com', config: {} }; it('does not allow admin subdomain', function () { expect(domains.validateHostname('my', domain)).to.be.an(Error); }); it('cannot have >63 length subdomains', function () { var s = Array(64).fill('s').join(''); expect(domains.validateHostname(s, domain)).to.be.an(Error); domain.zoneName = `dev.${s}.example.com`; expect(domains.validateHostname(`dev.${s}`, domain)).to.be.an(Error); }); it('allows only alphanumerics and hypen', function () { expect(domains.validateHostname('#2r', domain)).to.be.an(Error); expect(domains.validateHostname('a%b', domain)).to.be.an(Error); expect(domains.validateHostname('ab_', domain)).to.be.an(Error); expect(domains.validateHostname('ab.', domain)).to.be.an(Error); expect(domains.validateHostname('ab..c', domain)).to.be.an(Error); expect(domains.validateHostname('.ab', domain)).to.be.an(Error); expect(domains.validateHostname('-ab', domain)).to.be.an(Error); expect(domains.validateHostname('ab-', domain)).to.be.an(Error); }); it('total length cannot exceed 255', function () { var s = ''; for (var i = 0; i < (255 - 'example.com'.length); i++) s += 's'; expect(domains.validateHostname(s, domain)).to.be.an(Error); }); it('allow valid domains', function () { expect(domains.validateHostname('a', domain)).to.be(null); expect(domains.validateHostname('a0-x', domain)).to.be(null); expect(domains.validateHostname('a0.x', domain)).to.be(null); expect(domains.validateHostname('a0.x.y', domain)).to.be(null); expect(domains.validateHostname('01', domain)).to.be(null); }); }); describe('getName', function () { it('works with zoneName==domain', function () { const domain = { domain: 'example.com', zoneName: 'example.com', config: {} }; expect(domains.getName(domain, '', 'A')).to.be(''); expect(domains.getName(domain, 'www', 'A')).to.be('www'); expect(domains.getName(domain, 'www.dev', 'A')).to.be('www.dev'); expect(domains.getName(domain, '', 'MX')).to.be(''); expect(domains.getName(domain, '', 'TXT')).to.be(''); expect(domains.getName(domain, 'www', 'TXT')).to.be('www'); expect(domains.getName(domain, 'www.dev', 'TXT')).to.be('www.dev'); }); it('works when zoneName!=domain', function () { const domain = { domain: 'dev.example.com', zoneName: 'example.com', config: {} }; expect(domains.getName(domain, '', 'A')).to.be('dev'); expect(domains.getName(domain, 'www', 'A')).to.be('www.dev'); expect(domains.getName(domain, 'www.dev', 'A')).to.be('www.dev.dev'); expect(domains.getName(domain, '', 'MX')).to.be('dev'); expect(domains.getName(domain, '', 'TXT')).to.be('dev'); expect(domains.getName(domain, 'www', 'TXT')).to.be('www.dev'); expect(domains.getName(domain, 'www.dev', 'TXT')).to.be('www.dev.dev'); }); }); var awsHostedZones; it('registers subdomain', function (done) { awsHostedZones = { HostedZones: [{ Id: '/hostedzone/ZONEID', Name: `${DOMAIN_0.domain}.`, CallerReference: '305AFD59-9D73-4502-B020-F4E6F889CB30', ResourceRecordSetCount: 2, ChangeInfo: { Id: '/change/CKRTFJA0ANHXB', Status: 'INSYNC' } }], IsTruncated: false, MaxItems: '100' }; nock.cleanAll(); var awsScope = nock('http://localhost:5353') .get('/2013-04-01/hostedzonesbyname?dnsname=example.com.&maxitems=1') .times(2) .reply(200, js2xml('ListHostedZonesResponse', awsHostedZones, { wrapHandlers: { HostedZones: () => 'HostedZone'} })) .get('/2013-04-01/hostedzone/ZONEID/rrset?maxitems=1&name=applocation.' + DOMAIN_0.domain + '.&type=A') .reply(200, js2xml('ListResourceRecordSetsResponse', { ResourceRecordSets: [ ] }, { 'Content-Type': 'application/xml' })) .post('/2013-04-01/hostedzone/ZONEID/rrset/') .reply(200, js2xml('ChangeResourceRecordSetsResponse', { ChangeInfo: { Id: 'RRID', Status: 'INSYNC' } })); domains.registerLocations([ { subdomain: APP.location, domain: APP.domain } ], { overwriteDns: true }, (/*progress*/) => {}, function (error) { expect(error).to.be(null); expect(awsScope.isDone()).to.be.ok(); done(); }); }); it('unregisters subdomain', function (done) { nock.cleanAll(); var awsScope = nock('http://localhost:5353') .get('/2013-04-01/hostedzonesbyname?dnsname=example.com.&maxitems=1') .reply(200, js2xml('ListHostedZonesResponse', awsHostedZones, { wrapHandlers: { HostedZones: () => 'HostedZone'} })) .post('/2013-04-01/hostedzone/ZONEID/rrset/') .reply(200, js2xml('ChangeResourceRecordSetsResponse', { ChangeInfo: { Id: 'RRID', Status: 'INSYNC' } })); domains.unregisterLocations([ { subdomain: APP.location, domain: APP.domain } ], (/*progress*/) => {}, function (error) { expect(error).to.be(null); expect(awsScope.isDone()).to.be.ok(); done(); }); }); });