diff --git a/.npmignore b/.npmignore index 842e6c66..f068dc08 100644 --- a/.npmignore +++ b/.npmignore @@ -3,6 +3,7 @@ domains/ images/ public/ test/ +workspace/_exif/ workspace/_tmp/ log/ cache/ \ No newline at end of file diff --git a/config.js b/config.js index 7505b60c..682ea3bd 100755 --- a/config.js +++ b/config.js @@ -246,9 +246,10 @@ const schema = { allowDomainOverride: true }, path: { - doc: 'The remote host to request images from, for example http://media.example.com', + doc: 'The path to the assets directory', format: String, - default: './public' + default: './public', + allowDomainOverride: true } }, s3: { @@ -422,14 +423,14 @@ const schema = { clientId: { doc: 'Client ID used to access protected endpoints', format: String, - default: '1235488', + default: null, env: 'AUTH_TOKEN_ID', allowDomainOverride: true }, secret: { doc: 'Client secret used to access protected endpoints', format: String, - default: 'asd544see68e52', + default: null, env: 'AUTH_TOKEN_SECRET', allowDomainOverride: true }, @@ -444,7 +445,7 @@ const schema = { doc: 'Private key for signing JSON Web Tokens', format: String, env: 'AUTH_KEY', - default: 'YOU-MUST-CHANGE-ME-NOW!', + default: null, allowDomainOverride: true } }, diff --git a/config/config.test.json.sample b/config/config.test.json.sample index c174cbf5..a20021aa 100755 --- a/config/config.test.json.sample +++ b/config/config.test.json.sample @@ -67,7 +67,8 @@ }, "auth": { "clientId": "test", - "secret": "test" + "secret": "test", + "privateKey": "test" }, "cloudfront": { "accessKey": "", diff --git a/dadi/lib/auth/index.js b/dadi/lib/auth/index.js index 7369a77e..d7c02b90 100755 --- a/dadi/lib/auth/index.js +++ b/dadi/lib/auth/index.js @@ -64,6 +64,17 @@ module.exports = function (router) { let clientId = req.body.clientId let secret = req.body.secret + // Fail if the auth.clientId or auth.secret haven't been set. + if (!clientId || !secret) { + return fail('NoAccess', res) + } + + // Fail if the auth.privateKey hasn't been set. + if (!config.get('auth.privateKey')) { + return fail('NoPrivateKey', res) + } + + // Fail if the auth.clientId and auth.secret don't match the configured values. if ( clientId !== config.get('auth.clientId', req.__domain) || secret !== config.get('auth.secret', req.__domain) @@ -104,6 +115,9 @@ module.exports = function (router) { case 'InvalidToken': res.setHeader('WWW-Authenticate', 'Bearer, error="invalid_token", error_description="Invalid or expired access token"') break + case 'NoPrivateKey': + res.setHeader('WWW-Authenticate', 'Bearer, error="no_private_key", error_description="No private key configured in auth.privateKey"') + break default: res.setHeader('WWW-Authenticate', 'Bearer realm="/token"') } diff --git a/dadi/lib/controller/domain.js b/dadi/lib/controller/domain.js index e1b43ffd..b6a12d32 100644 --- a/dadi/lib/controller/domain.js +++ b/dadi/lib/controller/domain.js @@ -18,30 +18,8 @@ module.exports.post = (req, res) => { domains.forEach(item => { if (!DomainManager.getDomain(item.domain)) { - // Prepare the domain configuration. - let configContent = { - images: { - directory: { - enabled: false - }, - remote: { - enabled: true, - path: item.data.remote.path - } - }, - assets: { - directory: { - enabled: false - }, - remote: { - enabled: true, - path: item.data.remote.path - } - } - } - // Add the domain configuration. - DomainManager.addDomain(item.domain, configContent) + DomainManager.addDomain(item.domain, item.data) } }) @@ -55,11 +33,19 @@ module.exports.post = (req, res) => { * Accept PUT requests for modifying domains in the internal domain configuration. */ module.exports.put = (req, res) => { + // Don't accept an empty body + if (!req.body || !req.body.data) { + return help.sendBackJSON(400, { + success: false, + errors: ['Bad Request'] + }, res) + } + let domain = req.params.domain - let payload = req.body + let configSchema = req.body.data // Don't accept an empty param. - if (!domain || Object.keys(payload).length === 0) { + if (!domain || Object.keys(configSchema).length === 0) { return help.sendBackJSON(400, { success: false, errors: ['Bad Request'] @@ -74,30 +60,8 @@ module.exports.put = (req, res) => { }, res) } - // Prepare the domain configuration. - let configContent = { - images: { - directory: { - enabled: false - }, - remote: { - enabled: true, - path: payload.remote.path - } - }, - assets: { - directory: { - enabled: false - }, - remote: { - enabled: true, - path: payload.remote.path - } - } - } - // Update the domain configuration. - DomainManager.addDomain(domain, configContent) + DomainManager.addDomain(domain, configSchema) return help.sendBackJSON(200, { success: true, diff --git a/test/acceptance/auth.js b/test/acceptance/auth.js index 535b2b84..ab78a502 100755 --- a/test/acceptance/auth.js +++ b/test/acceptance/auth.js @@ -20,6 +20,13 @@ describe('Authentication', function () { }) }) + beforeEach(done => { + config.set('auth.clientId', 'test') + config.set('auth.secret', 'test') + config.set('auth.privateKey', 'test') + done() + }) + after(done => { app.stop(done) }) @@ -55,6 +62,41 @@ describe('Authentication', function () { .expect(401, done) }) + it('should not issue token if credentials are the null defaults', done => { + config.set('auth.clientId', null) + config.set('auth.secret', null) + + request(cdnUrl) + .post(tokenRoute) + .send({ + clientId: 'test123', + secret: 'badSecret', + code: ' ' + }) + .end((err, res) => { + res.statusCode.should.eql(401) + res.headers['www-authenticate'].should.eql('Bearer realm="/token"') + done() + }) + }) + + it('should not issue token if privateKey for token signing is not set', done => { + config.set('auth.privateKey', null) + + request(cdnUrl) + .post(tokenRoute) + .send({ + clientId: 'test123', + secret: 'badSecret', + code: ' ' + }) + .end((err, res) => { + res.statusCode.should.eql(401) + res.headers['www-authenticate'].should.eql('Bearer, error="no_private_key", error_description="No private key configured in auth.privateKey"') + done() + }) + }) + it('should allow `/api/flush` request containing token', done => { help.getBearerToken((err, token) => { request(cdnUrl) @@ -113,19 +155,27 @@ describe('Authentication', function () { config.set('multiDomain.directory', 'domains') config.loadDomainConfigs() + + config.set('auth.clientId', 'testxyz', 'testdomain.com') + config.set('auth.secret', 'testabc', 'testdomain.com') + config.set('auth.privateKey', 'test123', 'testdomain.com') }) after(() => { config.set('multiDomain.enabled', configBackup.multiDomain.enabled) config.set('multiDomain.directory', configBackup.multiDomain.directory) + + config.set('auth.clientId', 'test', 'testdomain.com') + config.set('auth.secret', 'test', 'testdomain.com') + config.set('auth.privateKey', 'test', 'testdomain.com') }) it('should encode the domain in the JWT', done => { request(cdnUrl) .post(tokenRoute) .send({ - clientId: 'test', - secret: 'test' + clientId: 'testxyz', + secret: 'testabc' }) .set('host', 'testdomain.com:80') .expect('content-type', 'application/json') @@ -139,7 +189,7 @@ describe('Authentication', function () { jwt.verify( res.body.accessToken, - config.get('auth.privateKey'), + config.get('auth.privateKey', 'testdomain.com'), (err, decoded) => { if (err) return done(err) @@ -155,8 +205,8 @@ describe('Authentication', function () { request(cdnUrl) .post(tokenRoute) .send({ - clientId: 'test', - secret: 'test' + clientId: 'testxyz', + secret: 'testabc' }) .set('host', 'testdomain.com:80') .expect('content-type', 'application/json') diff --git a/test/acceptance/cache.js b/test/acceptance/cache.js index 3e64bfdc..99129571 100644 --- a/test/acceptance/cache.js +++ b/test/acceptance/cache.js @@ -256,6 +256,10 @@ describe('Cache', function () { before(() => { config.set('multiDomain.enabled', true) config.loadDomainConfigs() + + config.set('auth.clientId', 'test') + config.set('auth.secret', 'test') + config.set('auth.privateKey', 'test') }) after(() => { diff --git a/test/acceptance/multi-domain.js b/test/acceptance/multi-domain.js index b0dd44c1..e18cc1ec 100644 --- a/test/acceptance/multi-domain.js +++ b/test/acceptance/multi-domain.js @@ -516,8 +516,10 @@ describe('Multi-domain', function () { { domain: 'api-added-domain.com', data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.com' + } } } } @@ -531,6 +533,8 @@ describe('Multi-domain', function () { res.statusCode.should.eql(201) let domainAdded = res.body.domains.includes('api-added-domain.com') domainAdded.should.eql(true) + + config.get('images.remote.path', 'api-added-domain.com').should.eql('https://google.com') done() }) }) @@ -542,16 +546,20 @@ describe('Multi-domain', function () { { domain: 'api-added-domain-one.com', data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.com' + } } } }, { domain: 'api-added-domain-two.com', data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.co.uk' + } } } } @@ -568,6 +576,12 @@ describe('Multi-domain', function () { res.body.domains.includes('api-added-domain-two.com') domainsAdded.should.eql(true) + + let addedPath = config.get('images.remote.path', 'api-added-domain-one.com') + addedPath.should.eql('https://google.com') + + addedPath = config.get('images.remote.path', 'api-added-domain-two.com') + addedPath.should.eql('https://google.co.uk') done() }) }) @@ -580,16 +594,22 @@ describe('Multi-domain', function () { { domain: domain, data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.com' + } } } } ] let update = { - remote: { - path: 'https://example.com' + data: { + images: { + remote: { + path: 'https://example.com' + } + } } } @@ -621,16 +641,22 @@ describe('Multi-domain', function () { { domain: domain, data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.com' + } } } } ] let update = { - remote: { - path: 'https://example.com' + data: { + images: { + remote: { + path: 'https://example.com' + } + } } } @@ -644,7 +670,7 @@ describe('Multi-domain', function () { domainAdded.should.eql(true) let configuredPath = config.get('images.remote.path', domain) - configuredPath.should.eql(domains[0].data.remote.path) + configuredPath.should.eql(domains[0].data.images.remote.path) request(cdnUrl) .put('/_dadi/domains/' + domain) @@ -656,7 +682,7 @@ describe('Multi-domain', function () { domainAdded.should.eql(true) configuredPath = config.get('images.remote.path', domain) - configuredPath.should.eql(update.remote.path) + configuredPath.should.eql(update.data.images.remote.path) done() }) }) @@ -684,8 +710,10 @@ describe('Multi-domain', function () { { domain: domain, data: { - remote: { - path: 'https://google.com' + images: { + remote: { + path: 'https://google.com' + } } } } @@ -701,7 +729,7 @@ describe('Multi-domain', function () { domainAdded.should.eql(true) let configuredPath = config.get('images.remote.path', domain) - configuredPath.should.eql(domains[0].data.remote.path) + configuredPath.should.eql(domains[0].data.images.remote.path) ;(typeof domainManager.getDomain(domain)).should.eql('object')