Certificates UI for all hosts, Access Lists placeholder, audit log tweaks

This commit is contained in:
Jamie Curnow
2018-08-16 13:08:56 +10:00
parent 6920a61871
commit 177bb2e888
27 changed files with 406 additions and 760 deletions

View File

@ -19,6 +19,7 @@ const internalAuditLog = {
let query = auditLogModel
.query()
.orderBy('created_on', 'DESC')
.orderBy('id', 'DESC')
.limit(100)
.allowEager('[user]');

View File

@ -1,11 +1,12 @@
'use strict';
const _ = require('lodash');
const error = require('../lib/error');
const deadHostModel = require('../models/dead_host');
const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const _ = require('lodash');
const error = require('../lib/error');
const deadHostModel = require('../models/dead_host');
const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
function omissions () {
return ['is_deleted'];
@ -19,6 +20,12 @@ const internalDeadHost = {
* @returns {Promise}
*/
create: (access, data) => {
let create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('dead_hosts:create', data)
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
@ -46,14 +53,40 @@ const internalDeadHost = {
.omit(omissions())
.insertAndFetch(data);
})
.then(row => {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, data)
.then(cert => {
// update host with cert id
return internalDeadHost.update(access, {
id: row.id,
certificate_id: cert.id
});
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then(row => {
// re-fetch with cert
return internalDeadHost.get(access, {
id: row.id,
expand: ['certificate', 'owner']
});
})
.then(row => {
// Configure nginx
return internalNginx.configure(deadHostModel, 'dead_host', row)
.then(() => {
return internalDeadHost.get(access, {id: row.id, expand: ['owner']});
return row;
});
})
.then(row => {
data.meta = _.assign({}, data.meta || {}, row.meta);
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
@ -71,11 +104,15 @@ const internalDeadHost = {
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {String} [data.email]
* @param {String} [data.name]
* @return {Promise}
*/
update: (access, data) => {
let create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('dead_hosts:update', data.id)
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
@ -105,13 +142,33 @@ const internalDeadHost = {
throw new error.InternalValidationError('404 Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then(cert => {
// update host with cert id
data.certificate_id = cert.id;
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then(row => {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, {
domain_names: row.domain_names
},data);
return deadHostModel
.query()
.omit(omissions())
.patchAndFetchById(row.id, data)
.where({id: data.id})
.patch(data)
.then(saved_row => {
saved_row.meta = internalHost.cleanMeta(saved_row.meta);
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
@ -123,6 +180,19 @@ const internalDeadHost = {
return _.omit(saved_row, omissions());
});
});
})
.then(() => {
return internalDeadHost.get(access, {
id: data.id,
expand: ['owner', 'certificate']
})
.then(row => {
// Configure nginx
return internalNginx.configure(deadHostModel, 'dead_host', row)
.then(() => {
return _.omit(row, omissions());
});
});
});
},
@ -165,7 +235,6 @@ const internalDeadHost = {
})
.then(row => {
if (row) {
row.meta = internalHost.cleanMeta(row.meta);
return _.omit(row, omissions());
} else {
throw new error.ItemNotFoundError(data.id);
@ -205,8 +274,6 @@ const internalDeadHost = {
})
.then(() => {
// Add to audit log
row.meta = internalHost.cleanMeta(row.meta);
return internalAuditLog.add(access, {
action: 'deleted',
object_type: 'dead-host',
@ -220,40 +287,6 @@ const internalDeadHost = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Object} data.files
* @returns {Promise}
*/
setCerts: (access, data) => {
return internalDeadHost.get(access, {id: data.id})
.then(row => {
_.map(data.files, (file, name) => {
if (internalHost.allowed_ssl_files.indexOf(name) !== -1) {
row.meta[name] = file.data.toString();
}
});
return internalDeadHost.update(access, {
id: data.id,
meta: row.meta
});
})
.then(row => {
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'dead-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.pick(row.meta, internalHost.allowed_ssl_files);
});
});
},
/**
* All Hosts
*
@ -289,13 +322,6 @@ const internalDeadHost = {
}
return query;
})
.then(rows => {
rows.map(row => {
row.meta = internalHost.cleanMeta(row.meta);
});
return rows;
});
},

View File

@ -105,8 +105,6 @@ const internalProxyHost = {
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {String} [data.email]
* @param {String} [data.name]
* @return {Promise}
*/
update: (access, data) => {
@ -162,6 +160,11 @@ const internalProxyHost = {
}
})
.then(row => {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, {
domain_names: row.domain_names
},data);
return proxyHostModel
.query()
.where({id: data.id})
@ -190,7 +193,7 @@ const internalProxyHost = {
.then(() => {
return _.omit(row, omissions());
});
})
});
});
},

View File

@ -6,6 +6,7 @@ const redirectionHostModel = require('../models/redirection_host');
const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
function omissions () {
return ['is_deleted'];
@ -19,6 +20,12 @@ const internalRedirectionHost = {
* @returns {Promise}
*/
create: (access, data) => {
let create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('redirection_hosts:create', data)
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
@ -46,14 +53,40 @@ const internalRedirectionHost = {
.omit(omissions())
.insertAndFetch(data);
})
.then(row => {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, data)
.then(cert => {
// update host with cert id
return internalRedirectionHost.update(access, {
id: row.id,
certificate_id: cert.id
});
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then(row => {
// re-fetch with cert
return internalRedirectionHost.get(access, {
id: row.id,
expand: ['certificate', 'owner']
});
})
.then(row => {
// Configure nginx
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
.then(() => {
return internalRedirectionHost.get(access, {id: row.id, expand: ['owner']});
return row;
});
})
.then(row => {
data.meta = _.assign({}, data.meta || {}, row.meta);
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
@ -71,11 +104,15 @@ const internalRedirectionHost = {
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {String} [data.email]
* @param {String} [data.name]
* @return {Promise}
*/
update: (access, data) => {
let create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('redirection_hosts:update', data.id)
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
@ -105,13 +142,33 @@ const internalRedirectionHost = {
throw new error.InternalValidationError('Redirection Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then(cert => {
// update host with cert id
data.certificate_id = cert.id;
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then(row => {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, {
domain_names: row.domain_names
},data);
return redirectionHostModel
.query()
.omit(omissions())
.patchAndFetchById(row.id, data)
.where({id: data.id})
.patch(data)
.then(saved_row => {
saved_row.meta = internalHost.cleanMeta(saved_row.meta);
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
@ -123,6 +180,19 @@ const internalRedirectionHost = {
return _.omit(saved_row, omissions());
});
});
})
.then(() => {
return internalRedirectionHost.get(access, {
id: data.id,
expand: ['owner', 'certificate']
})
.then(row => {
// Configure nginx
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
.then(() => {
return _.omit(row, omissions());
});
});
});
},
@ -165,7 +235,6 @@ const internalRedirectionHost = {
})
.then(row => {
if (row) {
row.meta = internalHost.cleanMeta(row.meta);
return _.omit(row, omissions());
} else {
throw new error.ItemNotFoundError(data.id);
@ -205,8 +274,6 @@ const internalRedirectionHost = {
})
.then(() => {
// Add to audit log
row.meta = internalHost.cleanMeta(row.meta);
return internalAuditLog.add(access, {
action: 'deleted',
object_type: 'redirection-host',
@ -220,40 +287,6 @@ const internalRedirectionHost = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Object} data.files
* @returns {Promise}
*/
setCerts: (access, data) => {
return internalRedirectionHost.get(access, {id: data.id})
.then(row => {
_.map(data.files, (file, name) => {
if (internalHost.allowed_ssl_files.indexOf(name) !== -1) {
row.meta[name] = file.data.toString();
}
});
return internalRedirectionHost.update(access, {
id: data.id,
meta: row.meta
});
})
.then(row => {
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'redirection-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.pick(row.meta, internalHost.allowed_ssl_files);
});
});
},
/**
* All Hosts
*
@ -289,13 +322,6 @@ const internalRedirectionHost = {
}
return query;
})
.then(rows => {
rows.map(row => {
row.meta = internalHost.cleanMeta(row.meta);
});
return rows;
});
},

View File

@ -57,8 +57,6 @@ const internalStream = {
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {String} [data.email]
* @param {String} [data.name]
* @return {Promise}
*/
update: (access, data) => {

View File

@ -147,38 +147,4 @@ router
.catch(next);
});
/**
* Specific dead-host Certificates
*
* /api/nginx/dead-hosts/123/certificates
*/
router
.route('/:host_id/certificates')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
/**
* POST /api/nginx/dead-hosts/123/certificates
*
* Upload certifications
*/
.post((req, res, next) => {
if (!req.files) {
res.status(400)
.send({error: 'No files were uploaded'});
} else {
internalDeadHost.setCerts(res.locals.access, {
id: parseInt(req.params.host_id, 10),
files: req.files
})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
}
});
module.exports = router;

View File

@ -147,38 +147,4 @@ router
.catch(next);
});
/**
* Specific redirection-host Certificates
*
* /api/nginx/redirection-hosts/123/certificates
*/
router
.route('/:host_id/certificates')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
/**
* POST /api/nginx/redirection-hosts/123/certificates
*
* Upload certifications
*/
.post((req, res, next) => {
if (!req.files) {
res.status(400)
.send({error: 'No files were uploaded'});
} else {
internalRedirectionHost.setCerts(res.locals.access, {
id: parseInt(req.params.host_id, 10),
files: req.files
})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
}
});
module.exports = router;

View File

@ -18,15 +18,12 @@
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"ssl_enabled": {
"$ref": "../definitions.json#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "../definitions.json#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "../definitions.json#/definitions/ssl_provider"
},
"meta": {
"type": "object",
"additionalProperties": false,
@ -54,15 +51,12 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -105,15 +99,12 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -142,15 +133,12 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}

View File

@ -26,15 +26,12 @@
"example": true,
"type": "boolean"
},
"ssl_enabled": {
"$ref": "../definitions.json#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "../definitions.json#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "../definitions.json#/definitions/ssl_provider"
},
"block_exploits": {
"$ref": "../definitions.json#/definitions/block_exploits"
},
@ -71,15 +68,12 @@
"preserve_path": {
"$ref": "#/definitions/preserve_path"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -132,15 +126,12 @@
"preserve_path": {
"$ref": "#/definitions/preserve_path"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -178,15 +169,12 @@
"preserve_path": {
"$ref": "#/definitions/preserve_path"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},