mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-04 00:13:33 +00:00
update nginx/dep updates/fix eslint/change line endings
Signed-off-by: Zoey <zoey@z0ey.de>
This commit is contained in:
@@ -1,67 +1,65 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const batchflow = require('batchflow');
|
||||
const logger = require('../logger').access;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const accessListModel = require('../models/access_list');
|
||||
const accessListAuthModel = require('../models/access_list_auth');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const batchflow = require('batchflow');
|
||||
const logger = require('../logger').access;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const accessListModel = require('../models/access_list');
|
||||
const accessListAuthModel = require('../models/access_list_auth');
|
||||
const accessListClientModel = require('../models/access_list_client');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalNginx = require('./nginx');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalNginx = require('./nginx');
|
||||
|
||||
function omissions () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalAccessList = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
return access.can('access_lists:create', data)
|
||||
.then((/*access_data*/) => {
|
||||
return access
|
||||
.can('access_lists:create', data)
|
||||
.then((/* access_data */) => {
|
||||
return accessListModel
|
||||
.query()
|
||||
.insertAndFetch({
|
||||
name: data.name,
|
||||
satisfy_any: data.satisfy_any,
|
||||
pass_auth: data.pass_auth,
|
||||
owner_user_id: access.token.getUserId(1)
|
||||
name: data.name,
|
||||
satisfy_any: data.satisfy_any,
|
||||
pass_auth: data.pass_auth,
|
||||
owner_user_id: access.token.getUserId(1),
|
||||
})
|
||||
.then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
data.id = row.id;
|
||||
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
// Now add the items
|
||||
data.items.map((item) => {
|
||||
promises.push(accessListAuthModel
|
||||
.query()
|
||||
.insert({
|
||||
promises.push(
|
||||
accessListAuthModel.query().insert({
|
||||
access_list_id: row.id,
|
||||
username: item.username,
|
||||
password: item.password
|
||||
})
|
||||
username: item.username,
|
||||
password: item.password,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// Now add the clients
|
||||
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||
data.clients.map((client) => {
|
||||
promises.push(accessListClientModel
|
||||
.query()
|
||||
.insert({
|
||||
promises.push(
|
||||
accessListClientModel.query().insert({
|
||||
access_list_id: row.id,
|
||||
address: client.address,
|
||||
directive: client.directive
|
||||
})
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -70,16 +68,21 @@ const internalAccessList = {
|
||||
})
|
||||
.then(() => {
|
||||
// re-fetch with expansions
|
||||
return internalAccessList.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
|
||||
}, true /* <- skip masking */);
|
||||
return internalAccessList.get(
|
||||
access,
|
||||
{
|
||||
id: data.id,
|
||||
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]'],
|
||||
},
|
||||
true /* <- skip masking */,
|
||||
);
|
||||
})
|
||||
.then((row) => {
|
||||
// Audit log
|
||||
data.meta = _.assign({}, data.meta || {}, row.meta);
|
||||
|
||||
return internalAccessList.build(row)
|
||||
return internalAccessList
|
||||
.build(row)
|
||||
.then(() => {
|
||||
if (row.proxy_host_count) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
@@ -88,10 +91,10 @@ const internalAccessList = {
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
action: 'created',
|
||||
object_type: 'access-list',
|
||||
object_id: row.id,
|
||||
meta: internalAccessList.maskItems(data)
|
||||
object_id: row.id,
|
||||
meta: internalAccessList.maskItems(data),
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -109,9 +112,10 @@ const internalAccessList = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
return access.can('access_lists:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
return internalAccessList.get(access, {id: data.id});
|
||||
return access
|
||||
.can('access_lists:update', data.id)
|
||||
.then((/* access_data */) => {
|
||||
return internalAccessList.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -122,31 +126,27 @@ const internalAccessList = {
|
||||
.then(() => {
|
||||
// patch name if specified
|
||||
if (typeof data.name !== 'undefined' && data.name) {
|
||||
return accessListModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.patch({
|
||||
name: data.name,
|
||||
satisfy_any: data.satisfy_any,
|
||||
pass_auth: data.pass_auth,
|
||||
});
|
||||
return accessListModel.query().where({ id: data.id }).patch({
|
||||
name: data.name,
|
||||
satisfy_any: data.satisfy_any,
|
||||
pass_auth: data.pass_auth,
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// Check for items and add/update/remove them
|
||||
if (typeof data.items !== 'undefined' && data.items) {
|
||||
let promises = [];
|
||||
let items_to_keep = [];
|
||||
const promises = [];
|
||||
const items_to_keep = [];
|
||||
|
||||
data.items.map(function (item) {
|
||||
if (item.password) {
|
||||
promises.push(accessListAuthModel
|
||||
.query()
|
||||
.insert({
|
||||
promises.push(
|
||||
accessListAuthModel.query().insert({
|
||||
access_list_id: data.id,
|
||||
username: item.username,
|
||||
password: item.password
|
||||
})
|
||||
username: item.username,
|
||||
password: item.password,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// This was supplied with an empty password, which means keep it but don't change the password
|
||||
@@ -154,79 +154,76 @@ const internalAccessList = {
|
||||
}
|
||||
});
|
||||
|
||||
let query = accessListAuthModel
|
||||
.query()
|
||||
.delete()
|
||||
.where('access_list_id', data.id);
|
||||
const query = accessListAuthModel.query().delete().where('access_list_id', data.id);
|
||||
|
||||
if (items_to_keep.length) {
|
||||
query.andWhere('username', 'NOT IN', items_to_keep);
|
||||
}
|
||||
|
||||
return query
|
||||
.then(() => {
|
||||
// Add new items
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
return query.then(() => {
|
||||
// Add new items
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// Check for clients and add/update/remove them
|
||||
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
data.clients.map(function (client) {
|
||||
if (client.address) {
|
||||
promises.push(accessListClientModel
|
||||
.query()
|
||||
.insert({
|
||||
promises.push(
|
||||
accessListClientModel.query().insert({
|
||||
access_list_id: data.id,
|
||||
address: client.address,
|
||||
directive: client.directive
|
||||
})
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let query = accessListClientModel
|
||||
.query()
|
||||
.delete()
|
||||
.where('access_list_id', data.id);
|
||||
const query = accessListClientModel.query().delete().where('access_list_id', data.id);
|
||||
|
||||
return query
|
||||
.then(() => {
|
||||
// Add new items
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
return query.then(() => {
|
||||
// Add new items
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
action: 'updated',
|
||||
object_type: 'access-list',
|
||||
object_id: data.id,
|
||||
meta: internalAccessList.maskItems(data)
|
||||
object_id: data.id,
|
||||
meta: internalAccessList.maskItems(data),
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// re-fetch with expansions
|
||||
return internalAccessList.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'items', 'clients', 'proxy_hosts.[certificate,access_list.[clients,items]]']
|
||||
}, true /* <- skip masking */);
|
||||
return internalAccessList.get(
|
||||
access,
|
||||
{
|
||||
id: data.id,
|
||||
expand: ['owner', 'items', 'clients', 'proxy_hosts.[certificate,access_list.[clients,items]]'],
|
||||
},
|
||||
true /* <- skip masking */,
|
||||
);
|
||||
})
|
||||
.then((row) => {
|
||||
return internalAccessList.build(row)
|
||||
return internalAccessList
|
||||
.build(row)
|
||||
.then(() => {
|
||||
if (row.proxy_host_count) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
}
|
||||
}).then(internalNginx.reload)
|
||||
})
|
||||
.then(internalNginx.reload)
|
||||
.then(() => {
|
||||
return internalAccessList.maskItems(row);
|
||||
});
|
||||
@@ -247,16 +244,10 @@ const internalAccessList = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('access_lists:get', data.id)
|
||||
return access
|
||||
.can('access_lists:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||
.where('access_list.is_deleted', 0)
|
||||
.andWhere('access_list.id', data.id)
|
||||
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
||||
.first();
|
||||
const query = accessListModel.query().select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count')).joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0').where('access_list.is_deleted', 0).andWhere('access_list.id', data.id).allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('access_list.owner_user_id', access.token.getUserId(1));
|
||||
@@ -291,9 +282,10 @@ const internalAccessList = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('access_lists:delete', data.id)
|
||||
return access
|
||||
.can('access_lists:delete', data.id)
|
||||
.then(() => {
|
||||
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']});
|
||||
return internalAccessList.get(access, { id: data.id, expand: ['proxy_hosts', 'items', 'clients'] });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -310,7 +302,7 @@ const internalAccessList = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// 2. update any proxy hosts that were using it (ignoring permissions)
|
||||
@@ -318,7 +310,7 @@ const internalAccessList = {
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where('access_list_id', '=', row.id)
|
||||
.patch({access_list_id: 0})
|
||||
.patch({ access_list_id: 0 })
|
||||
.then(() => {
|
||||
// 3. reconfigure those hosts, then reload nginx
|
||||
|
||||
@@ -336,21 +328,21 @@ const internalAccessList = {
|
||||
})
|
||||
.then(() => {
|
||||
// delete the htpasswd file
|
||||
let htpasswd_file = internalAccessList.getFilename(row);
|
||||
const htpasswd_file = internalAccessList.getFilename(row);
|
||||
|
||||
try {
|
||||
fs.unlinkSync(htpasswd_file);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// 4. audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'access-list',
|
||||
object_id: row.id,
|
||||
meta: _.omit(internalAccessList.maskItems(row), ['is_deleted', 'proxy_hosts'])
|
||||
object_id: row.id,
|
||||
meta: _.omit(internalAccessList.maskItems(row), ['is_deleted', 'proxy_hosts']),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -368,16 +360,10 @@ const internalAccessList = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('access_lists:list')
|
||||
return access
|
||||
.can('access_lists:list')
|
||||
.then((access_data) => {
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||
.where('access_list.is_deleted', 0)
|
||||
.groupBy('access_list.id')
|
||||
.allowGraph('[owner,items,clients]')
|
||||
.orderBy('access_list.name', 'ASC');
|
||||
const query = accessListModel.query().select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count')).joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0').where('access_list.is_deleted', 0).groupBy('access_list.id').allowGraph('[owner,items,clients]').orderBy('access_list.name', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('access_list.owner_user_id', access.token.getUserId(1));
|
||||
@@ -417,19 +403,15 @@ const internalAccessList = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = accessListModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -447,7 +429,7 @@ const internalAccessList = {
|
||||
first_char = val.password.charAt(0);
|
||||
}
|
||||
|
||||
list.items[idx].hint = first_char + ('*').repeat(repeat_for);
|
||||
list.items[idx].hint = first_char + '*'.repeat(repeat_for);
|
||||
list.items[idx].password = '';
|
||||
});
|
||||
}
|
||||
@@ -475,54 +457,55 @@ const internalAccessList = {
|
||||
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let htpasswd_file = internalAccessList.getFilename(list);
|
||||
const htpasswd_file = internalAccessList.getFilename(list);
|
||||
|
||||
// 1. remove any existing access file
|
||||
try {
|
||||
fs.unlinkSync(htpasswd_file);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// 2. create empty access file
|
||||
try {
|
||||
fs.writeFileSync(htpasswd_file, '', {encoding: 'utf8'});
|
||||
fs.writeFileSync(htpasswd_file, '', { encoding: 'utf8' });
|
||||
resolve(htpasswd_file);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
.then((htpasswd_file) => {
|
||||
// 3. generate password for each user
|
||||
if (list.items.length) {
|
||||
return new Promise((resolve, reject) => {
|
||||
batchflow(list.items).sequential()
|
||||
.each((i, item, next) => {
|
||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||
logger.info('Adding: ' + item.username);
|
||||
}).then((htpasswd_file) => {
|
||||
// 3. generate password for each user
|
||||
if (list.items.length) {
|
||||
return new Promise((resolve, reject) => {
|
||||
batchflow(list.items)
|
||||
.sequential()
|
||||
.each((i, item, next) => {
|
||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||
logger.info('Adding: ' + item.username);
|
||||
|
||||
utils.execFile('htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
||||
.then((/*result*/) => {
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
})
|
||||
.error((err) => {
|
||||
logger.error(err);
|
||||
reject(err);
|
||||
})
|
||||
.end((results) => {
|
||||
logger.success('Built Access file #' + list.id + ' for: ' + list.name);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
utils
|
||||
.execFile('htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
||||
.then((/* result */) => {
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
})
|
||||
.error((err) => {
|
||||
logger.error(err);
|
||||
reject(err);
|
||||
})
|
||||
.end((results) => {
|
||||
logger.success('Built Access file #' + list.id + ' for: ' + list.name);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalAccessList;
|
||||
|
@@ -1,8 +1,7 @@
|
||||
const error = require('../lib/error');
|
||||
const error = require('../lib/error');
|
||||
const auditLogModel = require('../models/audit-log');
|
||||
|
||||
const internalAuditLog = {
|
||||
|
||||
/**
|
||||
* All logs
|
||||
*
|
||||
@@ -12,28 +11,22 @@ const internalAuditLog = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('auditlog:list')
|
||||
.then(() => {
|
||||
let query = auditLogModel
|
||||
.query()
|
||||
.orderBy('created_on', 'DESC')
|
||||
.orderBy('id', 'DESC')
|
||||
.limit(100)
|
||||
.allowGraph('[user]');
|
||||
return access.can('auditlog:list').then(() => {
|
||||
const query = auditLogModel.query().orderBy('created_on', 'DESC').orderBy('id', 'DESC').limit(100).allowGraph('[user]');
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('meta', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('meta', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query;
|
||||
});
|
||||
return query;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -61,18 +54,18 @@ const internalAuditLog = {
|
||||
reject(new error.InternalValidationError('Audit log entry must contain an Action'));
|
||||
} else {
|
||||
// Make sure at least 1 of the IDs are set and action
|
||||
resolve(auditLogModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: data.user_id,
|
||||
action: data.action,
|
||||
resolve(
|
||||
auditLogModel.query().insert({
|
||||
user_id: data.user_id,
|
||||
action: data.action,
|
||||
object_type: data.object_type || '',
|
||||
object_id: data.object_id || 0,
|
||||
meta: data.meta || {}
|
||||
}));
|
||||
object_id: data.object_id || 0,
|
||||
meta: data.meta || {},
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalAuditLog;
|
||||
|
@@ -1,21 +1,21 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const moment = require('moment');
|
||||
const logger = require('../logger').ssl;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const moment = require('moment');
|
||||
const logger = require('../logger').ssl;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const certificateModel = require('../models/certificate');
|
||||
const dnsPlugins = require('../certbot-dns-plugins.json');
|
||||
const dnsPlugins = require('../certbot-dns-plugins.json');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalNginx = require('./nginx');
|
||||
const certbot = require('../lib/certbot');
|
||||
const archiver = require('archiver');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { isArray } = require('lodash');
|
||||
const internalNginx = require('./nginx');
|
||||
const certbot = require('../lib/certbot');
|
||||
const archiver = require('archiver');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { isArray } = require('lodash');
|
||||
|
||||
const certbotConfig = '/data/tls/certbot/config.ini';
|
||||
const certbotConfig = '/data/tls/certbot/config.ini';
|
||||
const certbotCommand = 'certbot --logs-dir /tmp/certbot-log --work-dir /tmp/certbot-work --config-dir /data/tls/certbot';
|
||||
|
||||
function omissions() {
|
||||
@@ -23,10 +23,9 @@ function omissions() {
|
||||
}
|
||||
|
||||
const internalCertificate = {
|
||||
|
||||
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
||||
intervalTimeout: 1000 * 60 * 60 * Number(process.env.CRT),
|
||||
interval: null,
|
||||
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
||||
intervalTimeout: 1000 * 60 * 60 * Number(process.env.CRT),
|
||||
interval: null,
|
||||
intervalProcessing: false,
|
||||
|
||||
initTimer: () => {
|
||||
@@ -42,22 +41,19 @@ const internalCertificate = {
|
||||
internalCertificate.intervalProcessing = true;
|
||||
logger.info('Renewing TLS certs close to expiry...');
|
||||
|
||||
const cmd = certbotCommand + ' renew --quiet ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew';
|
||||
const cmd = certbotCommand + ' renew --quiet ' + '--config "' + certbotConfig + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew';
|
||||
|
||||
return utils.exec(cmd)
|
||||
return utils
|
||||
.exec(cmd)
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
logger.info('Renew Result: ' + result);
|
||||
}
|
||||
|
||||
return internalNginx.reload()
|
||||
.then(() => {
|
||||
logger.info('Renew Complete');
|
||||
return result;
|
||||
});
|
||||
return internalNginx.reload().then(() => {
|
||||
logger.info('Renew Complete');
|
||||
return result;
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Now go and fetch all the certbot certs from the db and query the files and update expiry times
|
||||
@@ -67,24 +63,25 @@ const internalCertificate = {
|
||||
.andWhere('provider', 'letsencrypt')
|
||||
.then((certificates) => {
|
||||
if (certificates && certificates.length) {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
certificates.map(function (certificate) {
|
||||
promises.push(
|
||||
internalCertificate.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem')
|
||||
internalCertificate
|
||||
.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem')
|
||||
.then((cert_info) => {
|
||||
return certificateModel
|
||||
.query()
|
||||
.where('id', certificate.id)
|
||||
.andWhere('provider', 'letsencrypt')
|
||||
.patch({
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
// Don't want to stop the train here, just log the error
|
||||
logger.error(err.message);
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -108,7 +105,8 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
return access.can('certificates:create', data)
|
||||
return access
|
||||
.can('certificates:create', data)
|
||||
.then(() => {
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
|
||||
@@ -116,30 +114,29 @@ const internalCertificate = {
|
||||
data.nice_name = data.domain_names.join(', ');
|
||||
}
|
||||
|
||||
return certificateModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((certificate) => {
|
||||
if (certificate.provider === 'letsencrypt') {
|
||||
// Request a new Cert using Certbot. Let the fun begin.
|
||||
// Request a new Cert using Certbot. Let the fun begin.
|
||||
if (certificate.meta.dns_challenge) {
|
||||
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate)
|
||||
return internalCertificate
|
||||
.requestLetsEncryptSslWithDnsChallenge(certificate)
|
||||
.then(() => {
|
||||
return certificate;
|
||||
})
|
||||
.catch((err) => {
|
||||
// In the event of failure, throw err back
|
||||
// In the event of failure, throw err back
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
return internalCertificate.requestLetsEncryptSsl(certificate)
|
||||
return internalCertificate
|
||||
.requestLetsEncryptSsl(certificate)
|
||||
.then(() => {
|
||||
return certificate;
|
||||
})
|
||||
.catch((err) => {
|
||||
// In the event of failure, throw err back
|
||||
// In the event of failure, throw err back
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
@@ -152,16 +149,12 @@ const internalCertificate = {
|
||||
// At this point, the certbot cert should exist on disk.
|
||||
// Lets get the expiry date from the file and update the row silently
|
||||
return internalCertificate
|
||||
.getCertificateInfoFromFile(
|
||||
'/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem'
|
||||
)
|
||||
.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem')
|
||||
.then((cert_info) => {
|
||||
return certificateModel
|
||||
.query()
|
||||
.patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(cert_info.dates.to, 'X').format(
|
||||
'YYYY-MM-DD HH:mm:ss'
|
||||
),
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
|
||||
})
|
||||
.then((saved_row) => {
|
||||
// Add cert data for audit log
|
||||
@@ -171,7 +164,8 @@ const internalCertificate = {
|
||||
|
||||
return saved_row;
|
||||
});
|
||||
}).catch(async (error) => {
|
||||
})
|
||||
.catch(async (error) => {
|
||||
// Delete the certificate from the database if it was not created successfully
|
||||
await certificateModel.query().deleteById(certificate.id);
|
||||
|
||||
@@ -182,23 +176,22 @@ const internalCertificate = {
|
||||
}
|
||||
})
|
||||
.then((certificate) => {
|
||||
|
||||
data.meta = _.assign({}, data.meta || {}, certificate.meta);
|
||||
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'certificate',
|
||||
object_id: certificate.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'certificate',
|
||||
object_id: certificate.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return certificate;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
@@ -208,9 +201,10 @@ const internalCertificate = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
return access.can('certificates:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
return internalCertificate.get(access, {id: data.id});
|
||||
return access
|
||||
.can('certificates:update', data.id)
|
||||
.then((/* access_data */) => {
|
||||
return internalCertificate.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -224,7 +218,7 @@ const internalCertificate = {
|
||||
.then(utils.omitRow(omissions()))
|
||||
.then((saved_row) => {
|
||||
saved_row.meta = internalCertificate.cleanMeta(saved_row.meta);
|
||||
data.meta = internalCertificate.cleanMeta(data.meta);
|
||||
data.meta = internalCertificate.cleanMeta(data.meta);
|
||||
|
||||
// Add row.nice_name for custom certs
|
||||
if (saved_row.provider === 'other') {
|
||||
@@ -232,12 +226,13 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'certificate',
|
||||
object_id: row.id,
|
||||
meta: _.omit(data, ['expires_on']) // this prevents json circular reference because expires_on might be raw
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'certificate',
|
||||
object_id: row.id,
|
||||
meta: _.omit(data, ['expires_on']), // this prevents json circular reference because expires_on might be raw
|
||||
})
|
||||
.then(() => {
|
||||
return saved_row;
|
||||
});
|
||||
@@ -258,14 +253,10 @@ const internalCertificate = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('certificates:get', data.id)
|
||||
return access
|
||||
.can('certificates:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner]')
|
||||
.first();
|
||||
const query = certificateModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[owner]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -297,7 +288,8 @@ const internalCertificate = {
|
||||
*/
|
||||
download: (access, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
access.can('certificates:get', data)
|
||||
access
|
||||
.can('certificates:get', data)
|
||||
.then(() => {
|
||||
return internalCertificate.get(access, data);
|
||||
})
|
||||
@@ -309,45 +301,46 @@ const internalCertificate = {
|
||||
throw new error.ItemNotFoundError('Certificate ' + certificate.nice_name + ' does not exists');
|
||||
}
|
||||
|
||||
let certFiles = fs.readdirSync(zipDirectory)
|
||||
const certFiles = fs
|
||||
.readdirSync(zipDirectory)
|
||||
.filter((fn) => fn.endsWith('.pem'))
|
||||
.map((fn) => fs.realpathSync(path.join(zipDirectory, fn)));
|
||||
const downloadName = 'npm-' + data.id + '-' + `${Date.now()}.zip`;
|
||||
const opName = '/tmp/' + downloadName;
|
||||
internalCertificate.zipFiles(certFiles, opName)
|
||||
const opName = '/tmp/' + downloadName;
|
||||
internalCertificate
|
||||
.zipFiles(certFiles, opName)
|
||||
.then(() => {
|
||||
logger.debug('zip completed : ', opName);
|
||||
const resp = {
|
||||
fileName: opName
|
||||
fileName: opName,
|
||||
};
|
||||
resolve(resp);
|
||||
}).catch((err) => reject(err));
|
||||
})
|
||||
.catch((err) => reject(err));
|
||||
} else {
|
||||
throw new error.ValidationError('Only Certbot certificates can be downloaded');
|
||||
}
|
||||
}).catch((err) => reject(err));
|
||||
})
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} source
|
||||
* @param {String} out
|
||||
* @returns {Promise}
|
||||
*/
|
||||
* @param {String} source
|
||||
* @param {String} out
|
||||
* @returns {Promise}
|
||||
*/
|
||||
zipFiles(source, out) {
|
||||
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||
const stream = fs.createWriteStream(out);
|
||||
const stream = fs.createWriteStream(out);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
source
|
||||
.map((fl) => {
|
||||
let fileName = path.basename(fl);
|
||||
logger.debug(fl, 'added to certificate zip');
|
||||
archive.file(fl, { name: fileName });
|
||||
});
|
||||
archive
|
||||
.on('error', (err) => reject(err))
|
||||
.pipe(stream);
|
||||
source.map((fl) => {
|
||||
const fileName = path.basename(fl);
|
||||
logger.debug(fl, 'added to certificate zip');
|
||||
archive.file(fl, { name: fileName });
|
||||
});
|
||||
archive.on('error', (err) => reject(err)).pipe(stream);
|
||||
|
||||
stream.on('close', () => resolve());
|
||||
archive.finalize();
|
||||
@@ -362,9 +355,10 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('certificates:delete', data.id)
|
||||
return access
|
||||
.can('certificates:delete', data.id)
|
||||
.then(() => {
|
||||
return internalCertificate.get(access, {id: data.id});
|
||||
return internalCertificate.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -375,17 +369,17 @@ const internalCertificate = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
row.meta = internalCertificate.cleanMeta(row.meta);
|
||||
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'certificate',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -409,32 +403,26 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('certificates:list')
|
||||
.then((access_data) => {
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner]')
|
||||
.orderBy('nice_name', 'ASC');
|
||||
return access.can('certificates:list').then((access_data) => {
|
||||
const query = certificateModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[owner]').orderBy('nice_name', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('nice_name', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('nice_name', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -445,19 +433,15 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = certificateModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -504,18 +488,17 @@ const internalCertificate = {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -528,9 +511,9 @@ const internalCertificate = {
|
||||
*/
|
||||
createQuickCertificate: (access, data) => {
|
||||
return internalCertificate.create(access, {
|
||||
provider: 'letsencrypt',
|
||||
provider: 'letsencrypt',
|
||||
domain_names: data.domain_names,
|
||||
meta: data.meta
|
||||
meta: data.meta,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -545,7 +528,7 @@ const internalCertificate = {
|
||||
validate: (data) => {
|
||||
return new Promise((resolve) => {
|
||||
// Put file contents into an object
|
||||
let files = {};
|
||||
const files = {};
|
||||
_.map(data.files, (file, name) => {
|
||||
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||
files[name] = file.data.toString();
|
||||
@@ -553,13 +536,13 @@ const internalCertificate = {
|
||||
});
|
||||
|
||||
resolve(files);
|
||||
})
|
||||
.then((files) => {
|
||||
// For each file, create a temp file and write the contents to it
|
||||
// Then test it depending on the file type
|
||||
let promises = [];
|
||||
_.map(files, (content, type) => {
|
||||
promises.push(new Promise((resolve) => {
|
||||
}).then((files) => {
|
||||
// For each file, create a temp file and write the contents to it
|
||||
// Then test it depending on the file type
|
||||
const promises = [];
|
||||
_.map(files, (content, type) => {
|
||||
promises.push(
|
||||
new Promise((resolve) => {
|
||||
if (type === 'certificate_key') {
|
||||
resolve(internalCertificate.checkPrivateKey(content));
|
||||
} else {
|
||||
@@ -567,21 +550,21 @@ const internalCertificate = {
|
||||
resolve(internalCertificate.getCertificateInfo(content, true));
|
||||
}
|
||||
}).then((res) => {
|
||||
return {[type]: res};
|
||||
}));
|
||||
return { [type]: res };
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then((files) => {
|
||||
let data = {};
|
||||
|
||||
_.each(files, (file) => {
|
||||
data = _.assign({}, data, file);
|
||||
});
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((files) => {
|
||||
let data = {};
|
||||
|
||||
_.each(files, (file) => {
|
||||
data = _.assign({}, data, file);
|
||||
});
|
||||
|
||||
return data;
|
||||
});
|
||||
return data;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -592,40 +575,41 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
upload: (access, data) => {
|
||||
return internalCertificate.get(access, {id: data.id})
|
||||
.then((row) => {
|
||||
if (row.provider !== 'other') {
|
||||
throw new error.ValidationError('Cannot upload certificates for this type of provider');
|
||||
}
|
||||
return internalCertificate.get(access, { id: data.id }).then((row) => {
|
||||
if (row.provider !== 'other') {
|
||||
throw new error.ValidationError('Cannot upload certificates for this type of provider');
|
||||
}
|
||||
|
||||
return internalCertificate.validate(data)
|
||||
.then((validations) => {
|
||||
if (typeof validations.certificate === 'undefined') {
|
||||
throw new error.ValidationError('Certificate file was not provided');
|
||||
return internalCertificate
|
||||
.validate(data)
|
||||
.then((validations) => {
|
||||
if (typeof validations.certificate === 'undefined') {
|
||||
throw new error.ValidationError('Certificate file was not provided');
|
||||
}
|
||||
|
||||
_.map(data.files, (file, name) => {
|
||||
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||
row.meta[name] = file.data.toString();
|
||||
}
|
||||
|
||||
_.map(data.files, (file, name) => {
|
||||
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||
row.meta[name] = file.data.toString();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: This uses a mysql only raw function that won't translate to postgres
|
||||
return internalCertificate.update(access, {
|
||||
id: data.id,
|
||||
expires_on: moment(validations.certificate.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
|
||||
domain_names: [validations.certificate.cn],
|
||||
meta: _.clone(row.meta) // Prevent the update method from changing this value that we'll use later
|
||||
})
|
||||
.then((certificate) => {
|
||||
certificate.meta = row.meta;
|
||||
return internalCertificate.writeCustomCert(certificate);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return _.pick(row.meta, internalCertificate.allowedSslFiles);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: This uses a mysql only raw function that won't translate to postgres
|
||||
return internalCertificate
|
||||
.update(access, {
|
||||
id: data.id,
|
||||
expires_on: moment(validations.certificate.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
|
||||
domain_names: [validations.certificate.cn],
|
||||
meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
|
||||
})
|
||||
.then((certificate) => {
|
||||
certificate.meta = row.meta;
|
||||
return internalCertificate.writeCustomCert(certificate);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return _.pick(row.meta, internalCertificate.allowedSslFiles);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -636,7 +620,7 @@ const internalCertificate = {
|
||||
*/
|
||||
checkPrivateKey: (private_key) => {
|
||||
const randomName = crypto.randomBytes(8).toString('hex');
|
||||
const filepath = path.join('/tmp', 'certificate_' + randomName);
|
||||
const filepath = path.join('/tmp', 'certificate_' + randomName);
|
||||
fs.writeFileSync(filepath, private_key);
|
||||
return new Promise((resolve, reject) => {
|
||||
const failTimeout = setTimeout(() => {
|
||||
@@ -669,13 +653,15 @@ const internalCertificate = {
|
||||
*/
|
||||
getCertificateInfo: (certificate, throw_expired) => {
|
||||
const randomName = crypto.randomBytes(8).toString('hex');
|
||||
const filepath = path.join('/tmp', 'certificate_' + randomName);
|
||||
const filepath = path.join('/tmp', 'certificate_' + randomName);
|
||||
fs.writeFileSync(filepath, certificate);
|
||||
return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired)
|
||||
return internalCertificate
|
||||
.getCertificateInfoFromFile(filepath, throw_expired)
|
||||
.then((certData) => {
|
||||
fs.unlinkSync(filepath);
|
||||
return certData;
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
fs.unlinkSync(filepath);
|
||||
throw err;
|
||||
});
|
||||
@@ -689,9 +675,10 @@ const internalCertificate = {
|
||||
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
||||
*/
|
||||
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
||||
let certData = {};
|
||||
const certData = {};
|
||||
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
||||
return utils
|
||||
.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
||||
.then((result) => {
|
||||
// subject=CN = something.example.com
|
||||
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||
@@ -701,7 +688,7 @@ const internalCertificate = {
|
||||
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
|
||||
}
|
||||
|
||||
certData['cn'] = match[1];
|
||||
certData.cn = match[1];
|
||||
})
|
||||
.then(() => {
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
||||
@@ -714,7 +701,7 @@ const internalCertificate = {
|
||||
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
|
||||
}
|
||||
|
||||
certData['issuer'] = match[1];
|
||||
certData.issuer = match[1];
|
||||
})
|
||||
.then(() => {
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
||||
@@ -723,7 +710,7 @@ const internalCertificate = {
|
||||
// notBefore=Jul 14 04:04:29 2018 GMT
|
||||
// notAfter=Oct 12 04:04:29 2018 GMT
|
||||
let validFrom = null;
|
||||
let validTo = null;
|
||||
let validTo = null;
|
||||
|
||||
const lines = result.split('\n');
|
||||
lines.map(function (str) {
|
||||
@@ -749,13 +736,14 @@ const internalCertificate = {
|
||||
throw new error.ValidationError('Certificate has expired');
|
||||
}
|
||||
|
||||
certData['dates'] = {
|
||||
certData.dates = {
|
||||
from: validFrom,
|
||||
to: validTo
|
||||
to: validTo,
|
||||
};
|
||||
|
||||
return certData;
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err);
|
||||
});
|
||||
},
|
||||
@@ -789,12 +777,7 @@ const internalCertificate = {
|
||||
requestLetsEncryptSsl: (certificate) => {
|
||||
logger.info('Requesting Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
let cmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--authenticator webroot ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--domains "' + certificate.domain_names.join(',') + '"';
|
||||
let cmd = certbotCommand + ' certonly ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--authenticator webroot ' + '--preferred-challenges "dns,http" ' + '--domains "' + certificate.domain_names.join(',') + '"';
|
||||
|
||||
if (certificate.meta.letsencrypt_email === '') {
|
||||
cmd = cmd + ' --register-unsafely-without-email ';
|
||||
@@ -804,11 +787,10 @@ const internalCertificate = {
|
||||
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
return utils.exec(cmd)
|
||||
.then((result) => {
|
||||
logger.success(result);
|
||||
return result;
|
||||
});
|
||||
return utils.exec(cmd).then((result) => {
|
||||
logger.success(result);
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -825,20 +807,10 @@ const internalCertificate = {
|
||||
|
||||
const credentialsLocation = '/data/tls/certbot/credentials/credentials-' + certificate.id;
|
||||
// Escape single quotes and backslashes
|
||||
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
||||
const credentialsCmd = `echo '${escapedCredentials}' | tee '${credentialsLocation}'`;
|
||||
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll("'", "\\'").replaceAll('\\', '\\\\');
|
||||
const credentialsCmd = `echo '${escapedCredentials}' | tee '${credentialsLocation}'`;
|
||||
|
||||
let mainCmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||
'--authenticator ' + dnsPlugin.full_plugin_name + ' ' +
|
||||
'--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' +
|
||||
(
|
||||
certificate.meta.propagation_seconds !== undefined
|
||||
? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
||||
: ''
|
||||
);
|
||||
let mainCmd = certbotCommand + ' certonly ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--domains "' + certificate.domain_names.join(',') + '" ' + '--authenticator ' + dnsPlugin.full_plugin_name + ' ' + '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' + (certificate.meta.propagation_seconds !== undefined ? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds : '');
|
||||
|
||||
if (certificate.meta.letsencrypt_email === '') {
|
||||
mainCmd = mainCmd + ' --register-unsafely-without-email ';
|
||||
@@ -861,7 +833,6 @@ const internalCertificate = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
@@ -869,7 +840,8 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renew: (access, data) => {
|
||||
return access.can('certificates:update', data)
|
||||
return access
|
||||
.can('certificates:update', data)
|
||||
.then(() => {
|
||||
return internalCertificate.get(access, data);
|
||||
})
|
||||
@@ -882,20 +854,19 @@ const internalCertificate = {
|
||||
return internalCertificate.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem');
|
||||
})
|
||||
.then((cert_info) => {
|
||||
return certificateModel
|
||||
.query()
|
||||
.patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
||||
});
|
||||
return certificateModel.query().patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
|
||||
});
|
||||
})
|
||||
.then((updated_certificate) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'renewed',
|
||||
object_type: 'certificate',
|
||||
object_id: updated_certificate.id,
|
||||
meta: updated_certificate
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'renewed',
|
||||
object_type: 'certificate',
|
||||
object_id: updated_certificate.id,
|
||||
meta: updated_certificate,
|
||||
})
|
||||
.then(() => {
|
||||
return updated_certificate;
|
||||
});
|
||||
@@ -913,19 +884,14 @@ const internalCertificate = {
|
||||
renewLetsEncryptSsl: (certificate) => {
|
||||
logger.info('Renewing Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew';
|
||||
const cmd = certbotCommand + ' renew --force-renewal ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew';
|
||||
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
return utils.exec(cmd)
|
||||
.then((result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
});
|
||||
return utils.exec(cmd).then((result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -941,19 +907,14 @@ const internalCertificate = {
|
||||
|
||||
logger.info(`Renewing Certbot certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
|
||||
let mainCmd = certbotCommand + ' renew --force-renewal ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew';
|
||||
const mainCmd = certbotCommand + ' renew --force-renewal ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew';
|
||||
|
||||
logger.info('Command:', mainCmd);
|
||||
|
||||
return utils.exec(mainCmd)
|
||||
.then(async (result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
});
|
||||
return utils.exec(mainCmd).then(async (result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -964,18 +925,15 @@ const internalCertificate = {
|
||||
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
||||
logger.info('Revoking Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const mainCmd = certbotCommand + ' revoke ' +
|
||||
'--config "' + certbotConfig + '" ' +
|
||||
'--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/privkey.pem" ' +
|
||||
'--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
||||
'--delete-after-revoke';
|
||||
const mainCmd = certbotCommand + ' revoke ' + '--config "' + certbotConfig + '" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/privkey.pem" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem" ' + '--delete-after-revoke';
|
||||
|
||||
// Don't fail command if file does not exist
|
||||
const delete_credentialsCmd = `rm -f '/data/tls/certbot/credentials/credentials-${certificate.id}' || true`;
|
||||
|
||||
logger.info('Command:', mainCmd + '; ' + delete_credentialsCmd);
|
||||
|
||||
return utils.exec(mainCmd)
|
||||
return utils
|
||||
.exec(mainCmd)
|
||||
.then(async (result) => {
|
||||
await utils.exec(delete_credentialsCmd);
|
||||
logger.info(result);
|
||||
@@ -1009,7 +967,7 @@ const internalCertificate = {
|
||||
*/
|
||||
disableInUseHosts: (in_use_result) => {
|
||||
if (in_use_result.total_count) {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
if (in_use_result.proxy_hosts.length) {
|
||||
promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||
@@ -1024,7 +982,6 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -1039,7 +996,7 @@ const internalCertificate = {
|
||||
*/
|
||||
enableInUseHosts: (in_use_result) => {
|
||||
if (in_use_result.total_count) {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
if (in_use_result.proxy_hosts.length) {
|
||||
promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||
@@ -1054,7 +1011,6 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -1071,32 +1027,31 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
// Create a test challenge file
|
||||
const testChallengeDir = '/tmp/acme-challenge/.well-known/acme-challenge';
|
||||
const testChallengeDir = '/tmp/acme-challenge/.well-known/acme-challenge';
|
||||
const testChallengeFile = testChallengeDir + '/test-challenge';
|
||||
fs.mkdirSync(testChallengeDir, {recursive: true});
|
||||
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
|
||||
fs.mkdirSync(testChallengeDir, { recursive: true });
|
||||
fs.writeFileSync(testChallengeFile, 'Success', { encoding: 'utf8' });
|
||||
|
||||
async function performTestForDomain (domain) {
|
||||
async function performTestForDomain(domain) {
|
||||
logger.info('Testing http challenge for ' + domain);
|
||||
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
|
||||
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
|
||||
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&locationid=10`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(formBody),
|
||||
'Connection': 'keep-alive',
|
||||
'User-Agent': 'NPMplus',
|
||||
'Accept': '*/*'
|
||||
}
|
||||
Connection: 'keep-alive',
|
||||
'User-Agent': 'NPMplus',
|
||||
Accept: '*/*',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await new Promise((resolve) => {
|
||||
|
||||
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, function (res) {
|
||||
let responseBody = '';
|
||||
|
||||
res.on('data', (chunk) => responseBody = responseBody + chunk);
|
||||
res.on('data', (chunk) => (responseBody = responseBody + chunk));
|
||||
res.on('end', function () {
|
||||
try {
|
||||
const parsedBody = JSON.parse(responseBody + '');
|
||||
@@ -1120,8 +1075,10 @@ const internalCertificate = {
|
||||
// Make sure to write the request body.
|
||||
req.write(formBody);
|
||||
req.end();
|
||||
req.on('error', function (e) { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
||||
resolve(undefined); });
|
||||
req.on('error', function (e) {
|
||||
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
@@ -1154,7 +1111,7 @@ const internalCertificate = {
|
||||
|
||||
const results = {};
|
||||
|
||||
for (const domain of domains){
|
||||
for (const domain of domains) {
|
||||
results[domain] = await performTestForDomain(domain);
|
||||
}
|
||||
|
||||
@@ -1162,7 +1119,7 @@ const internalCertificate = {
|
||||
fs.unlinkSync(testChallengeFile);
|
||||
|
||||
return results;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalCertificate;
|
||||
|
@@ -1,66 +1,63 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
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 utils = require('../lib/utils');
|
||||
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 () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalDeadHost = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('dead_hosts:create', data)
|
||||
.then((/*access_data*/) => {
|
||||
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
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// At this point the domains should have been checked
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
|
||||
return deadHostModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return deadHostModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, data)
|
||||
return internalCertificate
|
||||
.createQuickCertificate(access, data)
|
||||
.then((cert) => {
|
||||
// update host with cert id
|
||||
return internalDeadHost.update(access, {
|
||||
id: row.id,
|
||||
certificate_id: cert.id
|
||||
id: row.id,
|
||||
certificate_id: cert.id,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -73,27 +70,27 @@ const internalDeadHost = {
|
||||
.then((row) => {
|
||||
// re-fetch with cert
|
||||
return internalDeadHost.get(access, {
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner']
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(deadHostModel, 'dead_host', row)
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
return internalNginx.configure(deadHostModel, 'dead_host', row).then(() => {
|
||||
return row;
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
data.meta = _.assign({}, data.meta || {}, row.meta);
|
||||
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
@@ -107,34 +104,34 @@ const internalDeadHost = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('dead_hosts:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
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
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
if (typeof data.domain_names !== 'undefined') {
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'dead', data.id));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {id: data.id});
|
||||
return internalDeadHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -143,10 +140,11 @@ const internalDeadHost = {
|
||||
}
|
||||
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, {
|
||||
domain_names: data.domain_names || row.domain_names,
|
||||
meta: _.assign({}, row.meta, data.meta)
|
||||
})
|
||||
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;
|
||||
@@ -160,42 +158,47 @@ const internalDeadHost = {
|
||||
})
|
||||
.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);
|
||||
data = _.assign(
|
||||
{},
|
||||
{
|
||||
domain_names: row.domain_names,
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
data = internalHost.cleanSslHstsData(data, row);
|
||||
|
||||
return deadHostModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.where({ id: data.id })
|
||||
.patch(data)
|
||||
.then((saved_row) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return _.omit(saved_row, omissions());
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate']
|
||||
})
|
||||
return internalDeadHost
|
||||
.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate'],
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(deadHostModel, 'dead_host', row)
|
||||
.then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
return internalNginx.configure(deadHostModel, 'dead_host', row).then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -213,14 +216,10 @@ const internalDeadHost = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('dead_hosts:get', data.id)
|
||||
return access
|
||||
.can('dead_hosts:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner,certificate]')
|
||||
.first();
|
||||
const query = deadHostModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[owner,certificate]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -252,9 +251,10 @@ const internalDeadHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('dead_hosts:delete', data.id)
|
||||
return access
|
||||
.can('dead_hosts:delete', data.id)
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {id: data.id});
|
||||
return internalDeadHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -265,22 +265,21 @@ const internalDeadHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('dead_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('dead_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -297,11 +296,12 @@ const internalDeadHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('dead_hosts:update', data.id)
|
||||
return access
|
||||
.can('dead_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner']
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -317,7 +317,7 @@ const internalDeadHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
enabled: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
@@ -326,10 +326,10 @@ const internalDeadHost = {
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
action: 'enabled',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -346,9 +346,10 @@ const internalDeadHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('dead_hosts:update', data.id)
|
||||
return access
|
||||
.can('dead_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {id: data.id});
|
||||
return internalDeadHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -363,22 +364,21 @@ const internalDeadHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
enabled: 0,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('dead_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('dead_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
action: 'disabled',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -396,14 +396,10 @@ const internalDeadHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('dead_hosts:list')
|
||||
return access
|
||||
.can('dead_hosts:list')
|
||||
.then((access_data) => {
|
||||
let query = deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
const query = deadHostModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[owner,certificate]').orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -439,20 +435,16 @@ const internalDeadHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = deadHostModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = deadHostModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
}
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalDeadHost;
|
||||
|
@@ -1,10 +1,9 @@
|
||||
const _ = require('lodash');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const _ = require('lodash');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const redirectionHostModel = require('../models/redirection_host');
|
||||
const deadHostModel = require('../models/dead_host');
|
||||
const deadHostModel = require('../models/dead_host');
|
||||
|
||||
const internalHost = {
|
||||
|
||||
/**
|
||||
* Makes sure that the ssl_* and hsts_* fields play nicely together.
|
||||
* ie: if there is no cert, then force_ssl is off.
|
||||
@@ -17,10 +16,10 @@ const internalHost = {
|
||||
cleanSslHstsData: function (data, existing_data) {
|
||||
existing_data = existing_data === undefined ? {} : existing_data;
|
||||
|
||||
let combined_data = _.assign({}, existing_data, data);
|
||||
const combined_data = _.assign({}, existing_data, data);
|
||||
|
||||
if (!combined_data.certificate_id) {
|
||||
combined_data.ssl_forced = false;
|
||||
combined_data.ssl_forced = false;
|
||||
combined_data.hsts_subdomains = false;
|
||||
}
|
||||
|
||||
@@ -69,47 +68,36 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getHostsWithDomains: function (domain_names) {
|
||||
let promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0),
|
||||
redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0),
|
||||
deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
];
|
||||
const promises = [proxyHostModel.query().where('is_deleted', 0), redirectionHostModel.query().where('is_deleted', 0), deadHostModel.query().where('is_deleted', 0)];
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((promises_results) => {
|
||||
let response_object = {
|
||||
total_count: 0,
|
||||
dead_hosts: [],
|
||||
proxy_hosts: [],
|
||||
redirection_hosts: []
|
||||
};
|
||||
return Promise.all(promises).then((promises_results) => {
|
||||
const response_object = {
|
||||
total_count: 0,
|
||||
dead_hosts: [],
|
||||
proxy_hosts: [],
|
||||
redirection_hosts: [],
|
||||
};
|
||||
|
||||
if (promises_results[0]) {
|
||||
// Proxy Hosts
|
||||
response_object.proxy_hosts = internalHost._getHostsWithDomains(promises_results[0], domain_names);
|
||||
response_object.total_count += response_object.proxy_hosts.length;
|
||||
}
|
||||
if (promises_results[0]) {
|
||||
// Proxy Hosts
|
||||
response_object.proxy_hosts = internalHost._getHostsWithDomains(promises_results[0], domain_names);
|
||||
response_object.total_count += response_object.proxy_hosts.length;
|
||||
}
|
||||
|
||||
if (promises_results[1]) {
|
||||
// Redirection Hosts
|
||||
response_object.redirection_hosts = internalHost._getHostsWithDomains(promises_results[1], domain_names);
|
||||
response_object.total_count += response_object.redirection_hosts.length;
|
||||
}
|
||||
if (promises_results[1]) {
|
||||
// Redirection Hosts
|
||||
response_object.redirection_hosts = internalHost._getHostsWithDomains(promises_results[1], domain_names);
|
||||
response_object.total_count += response_object.redirection_hosts.length;
|
||||
}
|
||||
|
||||
if (promises_results[2]) {
|
||||
// Dead Hosts
|
||||
response_object.dead_hosts = internalHost._getHostsWithDomains(promises_results[2], domain_names);
|
||||
response_object.total_count += response_object.dead_hosts.length;
|
||||
}
|
||||
if (promises_results[2]) {
|
||||
// Dead Hosts
|
||||
response_object.dead_hosts = internalHost._getHostsWithDomains(promises_results[2], domain_names);
|
||||
response_object.total_count += response_object.dead_hosts.length;
|
||||
}
|
||||
|
||||
return response_object;
|
||||
});
|
||||
return response_object;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -121,7 +109,7 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
||||
let promises = [
|
||||
const promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
@@ -133,39 +121,38 @@ const internalHost = {
|
||||
deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
];
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((promises_results) => {
|
||||
let is_taken = false;
|
||||
return Promise.all(promises).then((promises_results) => {
|
||||
let is_taken = false;
|
||||
|
||||
if (promises_results[0]) {
|
||||
// Proxy Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[0], ignore_type === 'proxy' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
if (promises_results[0]) {
|
||||
// Proxy Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[0], ignore_type === 'proxy' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (promises_results[1]) {
|
||||
// Redirection Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[1], ignore_type === 'redirection' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
if (promises_results[1]) {
|
||||
// Redirection Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[1], ignore_type === 'redirection' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (promises_results[2]) {
|
||||
// Dead Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[2], ignore_type === 'dead' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
if (promises_results[2]) {
|
||||
// Dead Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[2], ignore_type === 'dead' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hostname: hostname,
|
||||
is_taken: is_taken
|
||||
};
|
||||
});
|
||||
return {
|
||||
hostname,
|
||||
is_taken,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -203,7 +190,7 @@ const internalHost = {
|
||||
* @returns {Array}
|
||||
*/
|
||||
_getHostsWithDomains: function (hosts, domain_names) {
|
||||
let response = [];
|
||||
const response = [];
|
||||
|
||||
if (hosts && hosts.length) {
|
||||
hosts.map(function (host) {
|
||||
@@ -224,8 +211,7 @@ const internalHost = {
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalHost;
|
||||
|
@@ -1,11 +1,11 @@
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const logger = require('../logger').ip_ranges;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const logger = require('../logger').ip_ranges;
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const internalNginx = require('./nginx');
|
||||
|
||||
const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
|
||||
const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
|
||||
const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4';
|
||||
const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6';
|
||||
|
||||
@@ -13,11 +13,10 @@ const regIpV4 = /^(\d+\.?){4}\/\d+/;
|
||||
const regIpV6 = /^(([\da-fA-F]+)?:)+\/\d+/;
|
||||
|
||||
const internalIpRanges = {
|
||||
|
||||
interval_timeout: 1000 * 60 * 60 * Number(process.env.IPRT),
|
||||
interval: null,
|
||||
interval_timeout: 1000 * 60 * 60 * Number(process.env.IPRT),
|
||||
interval: null,
|
||||
interval_processing: false,
|
||||
iteration_count: 0,
|
||||
iteration_count: 0,
|
||||
|
||||
initTimer: () => {
|
||||
if (process.env.SKIP_IP_RANGES === 'false') {
|
||||
@@ -29,19 +28,21 @@ const internalIpRanges = {
|
||||
fetchUrl: (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Fetching ' + url);
|
||||
return https.get(url, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
let raw_data = '';
|
||||
res.on('data', (chunk) => {
|
||||
raw_data += chunk;
|
||||
});
|
||||
return https
|
||||
.get(url, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
let raw_data = '';
|
||||
res.on('data', (chunk) => {
|
||||
raw_data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
resolve(raw_data);
|
||||
res.on('end', () => {
|
||||
resolve(raw_data);
|
||||
});
|
||||
})
|
||||
.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -55,9 +56,10 @@ const internalIpRanges = {
|
||||
|
||||
let ip_ranges = [];
|
||||
|
||||
return internalIpRanges.fetchUrl(CLOUDFRONT_URL)
|
||||
return internalIpRanges
|
||||
.fetchUrl(CLOUDFRONT_URL)
|
||||
.then((cloudfront_data) => {
|
||||
let data = JSON.parse(cloudfront_data);
|
||||
const data = JSON.parse(cloudfront_data);
|
||||
|
||||
if (data && typeof data.prefixes !== 'undefined') {
|
||||
data.prefixes.map((item) => {
|
||||
@@ -79,31 +81,30 @@ const internalIpRanges = {
|
||||
return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
|
||||
})
|
||||
.then((cloudfare_data) => {
|
||||
let items = cloudfare_data.split('\n').filter((line) => regIpV4.test(line));
|
||||
ip_ranges = [... ip_ranges, ... items];
|
||||
const items = cloudfare_data.split('\n').filter((line) => regIpV4.test(line));
|
||||
ip_ranges = [...ip_ranges, ...items];
|
||||
})
|
||||
.then(() => {
|
||||
return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
|
||||
})
|
||||
.then((cloudfare_data) => {
|
||||
let items = cloudfare_data.split('\n').filter((line) => regIpV6.test(line));
|
||||
ip_ranges = [... ip_ranges, ... items];
|
||||
const items = cloudfare_data.split('\n').filter((line) => regIpV6.test(line));
|
||||
ip_ranges = [...ip_ranges, ...items];
|
||||
})
|
||||
.then(() => {
|
||||
let clean_ip_ranges = [];
|
||||
const clean_ip_ranges = [];
|
||||
ip_ranges.map((range) => {
|
||||
if (range) {
|
||||
clean_ip_ranges.push(range);
|
||||
}
|
||||
});
|
||||
|
||||
return internalIpRanges.generateConfig(clean_ip_ranges)
|
||||
.then(() => {
|
||||
if (internalIpRanges.iteration_count) {
|
||||
// Reload nginx
|
||||
return internalNginx.reload();
|
||||
}
|
||||
});
|
||||
return internalIpRanges.generateConfig(clean_ip_ranges).then(() => {
|
||||
if (internalIpRanges.iteration_count) {
|
||||
// Reload nginx
|
||||
return internalNginx.reload();
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
internalIpRanges.interval_processing = false;
|
||||
@@ -124,18 +125,18 @@ const internalIpRanges = {
|
||||
const renderEngine = utils.getRenderEngine();
|
||||
return new Promise((resolve, reject) => {
|
||||
let template = null;
|
||||
let filename = '/data/nginx/ip_ranges.conf';
|
||||
const filename = '/data/nginx/ip_ranges.conf';
|
||||
try {
|
||||
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', { encoding: 'utf8' });
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
renderEngine
|
||||
.parseAndRender(template, {ip_ranges: ip_ranges})
|
||||
.parseAndRender(template, { ip_ranges })
|
||||
.then((config_text) => {
|
||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||
fs.writeFileSync(filename, config_text, { encoding: 'utf8' });
|
||||
resolve(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -143,7 +144,7 @@ const internalIpRanges = {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalIpRanges;
|
||||
|
@@ -1,14 +1,13 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const logger = require('../logger').nginx;
|
||||
const config = require('../lib/config');
|
||||
const utils = require('../lib/utils');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const error = require('../lib/error');
|
||||
|
||||
const NgxPidFilePath = '/usr/local/nginx/logs/nginx.pid';
|
||||
|
||||
const internalNginx = {
|
||||
|
||||
/**
|
||||
* This will:
|
||||
* - test the nginx config first to make sure it's OK
|
||||
@@ -26,7 +25,8 @@ const internalNginx = {
|
||||
configure: (model, host_type, host) => {
|
||||
let combined_meta = {};
|
||||
|
||||
return internalNginx.test()
|
||||
return internalNginx
|
||||
.test()
|
||||
.then(() => {
|
||||
// Nginx is OK
|
||||
// We're deleting this config regardless.
|
||||
@@ -39,28 +39,26 @@ const internalNginx = {
|
||||
})
|
||||
.then(() => {
|
||||
// Test nginx again and update meta with result
|
||||
return internalNginx.test()
|
||||
return internalNginx
|
||||
.test()
|
||||
.then(() => {
|
||||
// nginx is ok
|
||||
combined_meta = _.assign({}, host.meta, {
|
||||
nginx_online: true,
|
||||
nginx_err: null
|
||||
nginx_err: null,
|
||||
});
|
||||
|
||||
return model
|
||||
.query()
|
||||
.where('id', host.id)
|
||||
.patch({
|
||||
meta: combined_meta
|
||||
});
|
||||
return model.query().where('id', host.id).patch({
|
||||
meta: combined_meta,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
// Remove the error_log line because it's a docker-ism false positive that doesn't need to be reported.
|
||||
// It will always look like this:
|
||||
// nginx: [alert] could not open error log file: open() "/dev/null" failed (6: No such device or address)
|
||||
|
||||
let valid_lines = [];
|
||||
let err_lines = err.message.split('\n');
|
||||
const valid_lines = [];
|
||||
const err_lines = err.message.split('\n');
|
||||
err_lines.map(function (line) {
|
||||
if (line.indexOf('/dev/null') === -1) {
|
||||
valid_lines.push(line);
|
||||
@@ -74,14 +72,14 @@ const internalNginx = {
|
||||
// config is bad, update meta and delete config
|
||||
combined_meta = _.assign({}, host.meta, {
|
||||
nginx_online: false,
|
||||
nginx_err: valid_lines.join('\n')
|
||||
nginx_err: valid_lines.join('\n'),
|
||||
});
|
||||
|
||||
return model
|
||||
.query()
|
||||
.where('id', host.id)
|
||||
.patch({
|
||||
meta: combined_meta
|
||||
meta: combined_meta,
|
||||
})
|
||||
.then(() => {
|
||||
internalNginx.renameConfigAsError(host_type, host);
|
||||
@@ -115,22 +113,21 @@ const internalNginx = {
|
||||
*/
|
||||
|
||||
reload: () => {
|
||||
return internalNginx.test()
|
||||
.then(() => {
|
||||
if (fs.existsSync(NgxPidFilePath)) {
|
||||
const ngxPID = fs.readFileSync(NgxPidFilePath, 'utf8').trim();
|
||||
if (ngxPID.length > 0) {
|
||||
logger.info('Reloading Nginx');
|
||||
utils.exec('nginx -s reload');
|
||||
} else {
|
||||
logger.info('Starting Nginx');
|
||||
utils.execfg('nginx -e stderr');
|
||||
}
|
||||
return internalNginx.test().then(() => {
|
||||
if (fs.existsSync(NgxPidFilePath)) {
|
||||
const ngxPID = fs.readFileSync(NgxPidFilePath, 'utf8').trim();
|
||||
if (ngxPID.length > 0) {
|
||||
logger.info('Reloading Nginx');
|
||||
utils.exec('nginx -s reload');
|
||||
} else {
|
||||
logger.info('Starting Nginx');
|
||||
utils.execfg('nginx -e stderr');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logger.info('Starting Nginx');
|
||||
utils.execfg('nginx -e stderr');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -155,22 +152,18 @@ const internalNginx = {
|
||||
let template;
|
||||
|
||||
try {
|
||||
template = fs.readFileSync(__dirname + '/../templates/_location.conf', {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/_location.conf', { encoding: 'utf8' });
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
const renderEngine = utils.getRenderEngine();
|
||||
const renderEngine = utils.getRenderEngine();
|
||||
let renderedLocations = '';
|
||||
|
||||
const locationRendering = async () => {
|
||||
for (let i = 0; i < host.locations.length; i++) {
|
||||
let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
||||
{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
|
||||
{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
|
||||
{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
|
||||
{certificate: host.certificate}, host.locations[i]);
|
||||
const locationCopy = Object.assign({}, { access_list_id: host.access_list_id }, { certificate_id: host.certificate_id }, { ssl_forced: host.ssl_forced }, { caching_enabled: host.caching_enabled }, { block_exploits: host.block_exploits }, { allow_websocket_upgrade: host.allow_websocket_upgrade }, { http2_support: host.http2_support }, { hsts_enabled: host.hsts_enabled }, { hsts_subdomains: host.hsts_subdomains }, { access_list: host.access_list }, { certificate: host.certificate }, host.locations[i]);
|
||||
|
||||
if (locationCopy.forward_host.indexOf('/') > -1) {
|
||||
const split = locationCopy.forward_host.split('/');
|
||||
@@ -179,14 +172,11 @@ const internalNginx = {
|
||||
locationCopy.forward_path = `/${split.join('/')}`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
renderedLocations += await renderEngine.parseAndRender(template, locationCopy);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
locationRendering().then(() => resolve(renderedLocations));
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
@@ -206,10 +196,10 @@ const internalNginx = {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let template = null;
|
||||
let filename = internalNginx.getConfigName(nice_host_type, host.id);
|
||||
const filename = internalNginx.getConfigName(nice_host_type, host.id);
|
||||
|
||||
try {
|
||||
template = fs.readFileSync(__dirname + '/../templates/' + nice_host_type + '.conf', {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/' + nice_host_type + '.conf', { encoding: 'utf8' });
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
@@ -227,8 +217,8 @@ const internalNginx = {
|
||||
}
|
||||
|
||||
if (host.locations) {
|
||||
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
||||
origLocations = [].concat(host.locations);
|
||||
// logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
||||
origLocations = [].concat(host.locations);
|
||||
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
||||
host.locations = renderedLocations;
|
||||
});
|
||||
@@ -239,7 +229,6 @@ const internalNginx = {
|
||||
host.use_default_location = false;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
locationsPromise = Promise.resolve();
|
||||
}
|
||||
@@ -251,7 +240,7 @@ const internalNginx = {
|
||||
renderEngine
|
||||
.parseAndRender(template, host)
|
||||
.then((config_text) => {
|
||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||
fs.writeFileSync(filename, config_text, { encoding: 'utf8' });
|
||||
|
||||
if (config.debug()) {
|
||||
logger.success('Wrote config:', filename, config_text);
|
||||
@@ -296,7 +285,6 @@ const internalNginx = {
|
||||
return host_type.replace(new RegExp('-', 'g'), '_');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {String} host_type
|
||||
* @param {Object} [host]
|
||||
@@ -304,10 +292,10 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
deleteConfig: (host_type, host, delete_err_file) => {
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file_err = config_file + '.err';
|
||||
|
||||
return new Promise((resolve/*, reject*/) => {
|
||||
return new Promise((resolve /*, reject */) => {
|
||||
internalNginx.deleteFile(config_file);
|
||||
if (delete_err_file) {
|
||||
internalNginx.deleteFile(config_file_err);
|
||||
@@ -322,10 +310,10 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renameConfigAsError: (host_type, host) => {
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file_err = config_file + '.err';
|
||||
|
||||
return new Promise((resolve/*, reject*/) => {
|
||||
return new Promise((resolve /*, reject */) => {
|
||||
fs.unlink(config_file, () => {
|
||||
// ignore result, continue
|
||||
fs.rename(config_file, config_file_err, () => {
|
||||
@@ -342,7 +330,7 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
bulkGenerateConfigs: (host_type, hosts) => {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
hosts.map(function (host) {
|
||||
promises.push(internalNginx.generateConfig(host_type, host));
|
||||
});
|
||||
@@ -356,7 +344,7 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
bulkDeleteConfigs: (host_type, hosts) => {
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
hosts.map(function (host) {
|
||||
promises.push(internalNginx.deleteConfig(host_type, host, true));
|
||||
});
|
||||
@@ -382,7 +370,7 @@ const internalNginx = {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalNginx;
|
||||
|
@@ -1,66 +1,63 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
|
||||
function omissions () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalProxyHost = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:create', data)
|
||||
return access
|
||||
.can('proxy_hosts:create', data)
|
||||
.then(() => {
|
||||
// Get a list of the domain names and check each of them against existing records
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// At this point the domains should have been checked
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return proxyHostModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, data)
|
||||
return internalCertificate
|
||||
.createQuickCertificate(access, data)
|
||||
.then((cert) => {
|
||||
// update host with cert id
|
||||
return internalProxyHost.update(access, {
|
||||
id: row.id,
|
||||
certificate_id: cert.id
|
||||
id: row.id,
|
||||
certificate_id: cert.id,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -73,28 +70,28 @@ const internalProxyHost = {
|
||||
.then((row) => {
|
||||
// re-fetch with cert
|
||||
return internalProxyHost.get(access, {
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner', 'access_list.[clients,items]']
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner', 'access_list.[clients,items]'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row)
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row).then(() => {
|
||||
return row;
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
// Audit log
|
||||
data.meta = _.assign({}, data.meta || {}, row.meta);
|
||||
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
@@ -108,34 +105,34 @@ const internalProxyHost = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
return access
|
||||
.can('proxy_hosts:update', data.id)
|
||||
.then((/* access_data */) => {
|
||||
// Get a list of the domain names and check each of them against existing records
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
if (typeof data.domain_names !== 'undefined') {
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'proxy', data.id));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
return internalProxyHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -144,10 +141,11 @@ const internalProxyHost = {
|
||||
}
|
||||
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, {
|
||||
domain_names: data.domain_names || row.domain_names,
|
||||
meta: _.assign({}, row.meta, data.meta)
|
||||
})
|
||||
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;
|
||||
@@ -161,47 +159,52 @@ 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);
|
||||
data = _.assign(
|
||||
{},
|
||||
{
|
||||
domain_names: row.domain_names,
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
data = internalHost.cleanSslHstsData(data, row);
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.where({ id: data.id })
|
||||
.patch(data)
|
||||
.then(utils.omitRow(omissions()))
|
||||
.then((saved_row) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return saved_row;
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate', 'access_list.[clients,items]']
|
||||
})
|
||||
return internalProxyHost
|
||||
.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate', 'access_list.[clients,items]'],
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row.enabled) {
|
||||
// No need to add nginx config if host is disabled
|
||||
return row;
|
||||
}
|
||||
// Configure nginx
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row)
|
||||
.then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row).then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -219,14 +222,10 @@ const internalProxyHost = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:get', data.id)
|
||||
return access
|
||||
.can('proxy_hosts:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner,access_list.[clients,items],certificate]')
|
||||
.first();
|
||||
const query = proxyHostModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[owner,access_list.[clients,items],certificate]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -259,9 +258,10 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('proxy_hosts:delete', data.id)
|
||||
return access
|
||||
.can('proxy_hosts:delete', data.id)
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
return internalProxyHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -272,22 +272,21 @@ const internalProxyHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('proxy_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('proxy_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -304,11 +303,12 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
return access
|
||||
.can('proxy_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner', 'access_list']
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner', 'access_list'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -324,7 +324,7 @@ const internalProxyHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
enabled: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
@@ -333,10 +333,10 @@ const internalProxyHost = {
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
action: 'enabled',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -353,9 +353,10 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
return access
|
||||
.can('proxy_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
return internalProxyHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -370,22 +371,21 @@ const internalProxyHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
enabled: 0,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('proxy_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('proxy_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
action: 'disabled',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -403,14 +403,10 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('proxy_hosts:list')
|
||||
return access
|
||||
.can('proxy_hosts:list')
|
||||
.then((access_data) => {
|
||||
let query = proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,access_list,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
const query = proxyHostModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[owner,access_list,certificate]').orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -446,20 +442,16 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = proxyHostModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = proxyHostModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
}
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalProxyHost;
|
||||
|
@@ -1,66 +1,63 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const redirectionHostModel = require('../models/redirection_host');
|
||||
const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
|
||||
function omissions () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalRedirectionHost = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('redirection_hosts:create', data)
|
||||
.then((/*access_data*/) => {
|
||||
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
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// At this point the domains should have been checked
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
|
||||
return redirectionHostModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return redirectionHostModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, data)
|
||||
return internalCertificate
|
||||
.createQuickCertificate(access, data)
|
||||
.then((cert) => {
|
||||
// update host with cert id
|
||||
return internalRedirectionHost.update(access, {
|
||||
id: row.id,
|
||||
certificate_id: cert.id
|
||||
id: row.id,
|
||||
certificate_id: cert.id,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -72,27 +69,27 @@ const internalRedirectionHost = {
|
||||
.then((row) => {
|
||||
// re-fetch with cert
|
||||
return internalRedirectionHost.get(access, {
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner']
|
||||
id: row.id,
|
||||
expand: ['certificate', 'owner'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
return internalNginx.configure(redirectionHostModel, 'redirection_host', row).then(() => {
|
||||
return row;
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
data.meta = _.assign({}, data.meta || {}, row.meta);
|
||||
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
@@ -106,34 +103,34 @@ const internalRedirectionHost = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
let create_certificate = data.certificate_id === 'new';
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('redirection_hosts:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
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
|
||||
let domain_name_check_promises = [];
|
||||
const domain_name_check_promises = [];
|
||||
|
||||
if (typeof data.domain_names !== 'undefined') {
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'redirection', data.id));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
return Promise.all(domain_name_check_promises).then((check_results) => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {id: data.id});
|
||||
return internalRedirectionHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -142,10 +139,11 @@ const internalRedirectionHost = {
|
||||
}
|
||||
|
||||
if (create_certificate) {
|
||||
return internalCertificate.createQuickCertificate(access, {
|
||||
domain_names: data.domain_names || row.domain_names,
|
||||
meta: _.assign({}, row.meta, data.meta)
|
||||
})
|
||||
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;
|
||||
@@ -159,42 +157,47 @@ const internalRedirectionHost = {
|
||||
})
|
||||
.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);
|
||||
data = _.assign(
|
||||
{},
|
||||
{
|
||||
domain_names: row.domain_names,
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
data = internalHost.cleanSslHstsData(data, row);
|
||||
|
||||
return redirectionHostModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.where({ id: data.id })
|
||||
.patch(data)
|
||||
.then((saved_row) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return _.omit(saved_row, omissions());
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate']
|
||||
})
|
||||
return internalRedirectionHost
|
||||
.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner', 'certificate'],
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
|
||||
.then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
return internalNginx.configure(redirectionHostModel, 'redirection_host', row).then((new_meta) => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -212,14 +215,10 @@ const internalRedirectionHost = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('redirection_hosts:get', data.id)
|
||||
return access
|
||||
.can('redirection_hosts:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner,certificate]')
|
||||
.first();
|
||||
const query = redirectionHostModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[owner,certificate]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -252,9 +251,10 @@ const internalRedirectionHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('redirection_hosts:delete', data.id)
|
||||
return access
|
||||
.can('redirection_hosts:delete', data.id)
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {id: data.id});
|
||||
return internalRedirectionHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -265,22 +265,21 @@ const internalRedirectionHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('redirection_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('redirection_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -297,11 +296,12 @@ const internalRedirectionHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('redirection_hosts:update', data.id)
|
||||
return access
|
||||
.can('redirection_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner']
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -317,7 +317,7 @@ const internalRedirectionHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
enabled: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
@@ -326,10 +326,10 @@ const internalRedirectionHost = {
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
action: 'enabled',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -346,9 +346,10 @@ const internalRedirectionHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('redirection_hosts:update', data.id)
|
||||
return access
|
||||
.can('redirection_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {id: data.id});
|
||||
return internalRedirectionHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -363,22 +364,21 @@ const internalRedirectionHost = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
enabled: 0,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('redirection_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('redirection_host', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
action: 'disabled',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -396,14 +396,10 @@ const internalRedirectionHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('redirection_hosts:list')
|
||||
return access
|
||||
.can('redirection_hosts:list')
|
||||
.then((access_data) => {
|
||||
let query = redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
const query = redirectionHostModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[owner,certificate]').orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -439,20 +435,16 @@ const internalRedirectionHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = redirectionHostModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = redirectionHostModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
}
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalRedirectionHost;
|
||||
|
@@ -1,38 +1,32 @@
|
||||
const internalProxyHost = require('./proxy-host');
|
||||
const internalProxyHost = require('./proxy-host');
|
||||
const internalRedirectionHost = require('./redirection-host');
|
||||
const internalDeadHost = require('./dead-host');
|
||||
const internalStream = require('./stream');
|
||||
const internalDeadHost = require('./dead-host');
|
||||
const internalStream = require('./stream');
|
||||
|
||||
const internalReport = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @return {Promise}
|
||||
*/
|
||||
getHostsReport: (access) => {
|
||||
return access.can('reports:hosts', 1)
|
||||
return access
|
||||
.can('reports:hosts', 1)
|
||||
.then((access_data) => {
|
||||
let user_id = access.token.getUserId(1);
|
||||
const user_id = access.token.getUserId(1);
|
||||
|
||||
let promises = [
|
||||
internalProxyHost.getCount(user_id, access_data.visibility),
|
||||
internalRedirectionHost.getCount(user_id, access_data.visibility),
|
||||
internalStream.getCount(user_id, access_data.visibility),
|
||||
internalDeadHost.getCount(user_id, access_data.visibility)
|
||||
];
|
||||
const promises = [internalProxyHost.getCount(user_id, access_data.visibility), internalRedirectionHost.getCount(user_id, access_data.visibility), internalStream.getCount(user_id, access_data.visibility), internalDeadHost.getCount(user_id, access_data.visibility)];
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then((counts) => {
|
||||
return {
|
||||
proxy: counts.shift(),
|
||||
proxy: counts.shift(),
|
||||
redirection: counts.shift(),
|
||||
stream: counts.shift(),
|
||||
dead: counts.shift()
|
||||
stream: counts.shift(),
|
||||
dead: counts.shift(),
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalReport;
|
||||
|
@@ -1,10 +1,9 @@
|
||||
const fs = require('fs');
|
||||
const error = require('../lib/error');
|
||||
const settingModel = require('../models/setting');
|
||||
const fs = require('fs');
|
||||
const error = require('../lib/error');
|
||||
const settingModel = require('../models/setting');
|
||||
const internalNginx = require('./nginx');
|
||||
|
||||
const internalSetting = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
@@ -12,9 +11,10 @@ const internalSetting = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
return access.can('settings:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
return internalSetting.get(access, {id: data.id});
|
||||
return access
|
||||
.can('settings:update', data.id)
|
||||
.then((/* access_data */) => {
|
||||
return internalSetting.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -22,25 +22,23 @@ const internalSetting = {
|
||||
throw new error.InternalValidationError('Setting could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||
}
|
||||
|
||||
return settingModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.patch(data);
|
||||
return settingModel.query().where({ id: data.id }).patch(data);
|
||||
})
|
||||
.then(() => {
|
||||
return internalSetting.get(access, {
|
||||
id: data.id
|
||||
id: data.id,
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id === 'default-site') {
|
||||
// write the html if we need to
|
||||
if (row.value === 'html') {
|
||||
fs.writeFileSync('/data/nginx/etc/index.html', row.meta.html, {encoding: 'utf8'});
|
||||
fs.writeFileSync('/data/nginx/etc/index.html', row.meta.html, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
// Configure nginx
|
||||
return internalNginx.deleteConfig('default')
|
||||
return internalNginx
|
||||
.deleteConfig('default')
|
||||
.then(() => {
|
||||
return internalNginx.generateConfig('default', row);
|
||||
})
|
||||
@@ -53,8 +51,9 @@ const internalSetting = {
|
||||
.then(() => {
|
||||
return row;
|
||||
})
|
||||
.catch((/*err*/) => {
|
||||
internalNginx.deleteConfig('default')
|
||||
.catch((/* err */) => {
|
||||
internalNginx
|
||||
.deleteConfig('default')
|
||||
.then(() => {
|
||||
return internalNginx.test();
|
||||
})
|
||||
@@ -79,12 +78,10 @@ const internalSetting = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
get: (access, data) => {
|
||||
return access.can('settings:get', data.id)
|
||||
return access
|
||||
.can('settings:get', data.id)
|
||||
.then(() => {
|
||||
return settingModel
|
||||
.query()
|
||||
.where('id', data.id)
|
||||
.first();
|
||||
return settingModel.query().where('id', data.id).first();
|
||||
})
|
||||
.then((row) => {
|
||||
if (row) {
|
||||
@@ -102,12 +99,10 @@ const internalSetting = {
|
||||
* @returns {*}
|
||||
*/
|
||||
getCount: (access) => {
|
||||
return access.can('settings:list')
|
||||
return access
|
||||
.can('settings:list')
|
||||
.then(() => {
|
||||
return settingModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.first();
|
||||
return settingModel.query().count('id as count').first();
|
||||
})
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
@@ -121,13 +116,10 @@ const internalSetting = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access) => {
|
||||
return access.can('settings:list')
|
||||
.then(() => {
|
||||
return settingModel
|
||||
.query()
|
||||
.orderBy('description', 'ASC');
|
||||
});
|
||||
}
|
||||
return access.can('settings:list').then(() => {
|
||||
return settingModel.query().orderBy('description', 'ASC');
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalSetting;
|
||||
|
@@ -1,24 +1,24 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const streamModel = require('../models/stream');
|
||||
const internalNginx = require('./nginx');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const streamModel = require('../models/stream');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
|
||||
function omissions () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalStream = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
return access.can('streams:create', data)
|
||||
.then((/*access_data*/) => {
|
||||
return access
|
||||
.can('streams:create', data)
|
||||
.then((/* access_data */) => {
|
||||
// TODO: At this point the existing ports should have been checked
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
|
||||
@@ -26,26 +26,23 @@ const internalStream = {
|
||||
data.meta = {};
|
||||
}
|
||||
|
||||
return streamModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return streamModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(streamModel, 'stream', row)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
||||
});
|
||||
return internalNginx.configure(streamModel, 'stream', row).then(() => {
|
||||
return internalStream.get(access, { id: row.id, expand: ['owner'] });
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return row;
|
||||
});
|
||||
@@ -59,10 +56,11 @@ const internalStream = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
return access.can('streams:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
return access
|
||||
.can('streams:update', data.id)
|
||||
.then((/* access_data */) => {
|
||||
// TODO: at this point the existing streams should have been checked
|
||||
return internalStream.get(access, {id: data.id});
|
||||
return internalStream.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
@@ -75,19 +73,19 @@ const internalStream = {
|
||||
.patchAndFetchById(row.id, data)
|
||||
.then(utils.omitRow(omissions()))
|
||||
.then((saved_row) => {
|
||||
return internalNginx.configure(streamModel, 'stream', saved_row)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
||||
});
|
||||
return internalNginx.configure(streamModel, 'stream', saved_row).then(() => {
|
||||
return internalStream.get(access, { id: row.id, expand: ['owner'] });
|
||||
});
|
||||
})
|
||||
.then((saved_row) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return saved_row;
|
||||
});
|
||||
@@ -108,14 +106,10 @@ const internalStream = {
|
||||
data = {};
|
||||
}
|
||||
|
||||
return access.can('streams:get', data.id)
|
||||
return access
|
||||
.can('streams:get', data.id)
|
||||
.then((access_data) => {
|
||||
let query = streamModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner]')
|
||||
.first();
|
||||
const query = streamModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[owner]').first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
@@ -147,9 +141,10 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('streams:delete', data.id)
|
||||
return access
|
||||
.can('streams:delete', data.id)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: data.id});
|
||||
return internalStream.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -160,22 +155,21 @@ const internalStream = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('stream', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('stream', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -192,11 +186,12 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('streams:update', data.id)
|
||||
return access
|
||||
.can('streams:update', data.id)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner']
|
||||
id: data.id,
|
||||
expand: ['owner'],
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -212,7 +207,7 @@ const internalStream = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
enabled: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
@@ -221,10 +216,10 @@ const internalStream = {
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
action: 'enabled',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -241,9 +236,10 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('streams:update', data.id)
|
||||
return access
|
||||
.can('streams:update', data.id)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: data.id});
|
||||
return internalStream.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row) {
|
||||
@@ -258,22 +254,21 @@ const internalStream = {
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
enabled: 0,
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('stream', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
return internalNginx.deleteConfig('stream', row).then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
action: 'disabled',
|
||||
object_type: 'stream-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -291,32 +286,26 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('streams:list')
|
||||
.then((access_data) => {
|
||||
let query = streamModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner]')
|
||||
.orderBy('incoming_port', 'ASC');
|
||||
return access.can('streams:list').then((access_data) => {
|
||||
const query = streamModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[owner]').orderBy('incoming_port', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('incoming_port', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('incoming_port', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -327,20 +316,16 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = streamModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
const query = streamModel.query().count('id as count').where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', user_id);
|
||||
}
|
||||
|
||||
return query.first()
|
||||
.then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
}
|
||||
return query.first().then((row) => {
|
||||
return parseInt(row.count, 10);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalStream;
|
||||
|
@@ -1,12 +1,11 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const userModel = require('../models/user');
|
||||
const authModel = require('../models/auth');
|
||||
const helpers = require('../lib/helpers');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const userModel = require('../models/user');
|
||||
const authModel = require('../models/auth');
|
||||
const helpers = require('../lib/helpers');
|
||||
const TokenModel = require('../models/token');
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {String} data.identity
|
||||
@@ -17,9 +16,9 @@ module.exports = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getTokenFromEmail: (data, issuer) => {
|
||||
let Token = new TokenModel();
|
||||
const Token = new TokenModel();
|
||||
|
||||
data.scope = data.scope || 'user';
|
||||
data.scope = data.scope || 'user';
|
||||
data.expiry = data.expiry || '1d';
|
||||
|
||||
return userModel
|
||||
@@ -38,40 +37,37 @@ module.exports = {
|
||||
.first()
|
||||
.then((auth) => {
|
||||
if (auth) {
|
||||
return auth.verifyPassword(data.secret)
|
||||
.then((valid) => {
|
||||
if (valid) {
|
||||
|
||||
if (data.scope !== 'user' && _.indexOf(user.roles, data.scope) === -1) {
|
||||
// The scope requested doesn't exist as a role against the user,
|
||||
// you shall not pass.
|
||||
throw new error.AuthError('Invalid scope: ' + data.scope);
|
||||
}
|
||||
|
||||
// Create a moment of the expiry expression
|
||||
let expiry = helpers.parseDatePeriod(data.expiry);
|
||||
if (expiry === null) {
|
||||
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
||||
}
|
||||
|
||||
return Token.create({
|
||||
iss: issuer || 'api',
|
||||
attrs: {
|
||||
id: user.id
|
||||
},
|
||||
scope: [data.scope],
|
||||
expiresIn: data.expiry
|
||||
})
|
||||
.then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString()
|
||||
};
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('Invalid password');
|
||||
return auth.verifyPassword(data.secret).then((valid) => {
|
||||
if (valid) {
|
||||
if (data.scope !== 'user' && _.indexOf(user.roles, data.scope) === -1) {
|
||||
// The scope requested doesn't exist as a role against the user,
|
||||
// you shall not pass.
|
||||
throw new error.AuthError('Invalid scope: ' + data.scope);
|
||||
}
|
||||
});
|
||||
|
||||
// Create a moment of the expiry expression
|
||||
const expiry = helpers.parseDatePeriod(data.expiry);
|
||||
if (expiry === null) {
|
||||
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
||||
}
|
||||
|
||||
return Token.create({
|
||||
iss: issuer || 'api',
|
||||
attrs: {
|
||||
id: user.id,
|
||||
},
|
||||
scope: [data.scope],
|
||||
expiresIn: data.expiry,
|
||||
}).then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString(),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('Invalid password');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('No password auth for user');
|
||||
}
|
||||
@@ -90,21 +86,20 @@ module.exports = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getFreshToken: (access, data) => {
|
||||
let Token = new TokenModel();
|
||||
const Token = new TokenModel();
|
||||
|
||||
data = data || {};
|
||||
data = data || {};
|
||||
data.expiry = data.expiry || '1d';
|
||||
|
||||
if (access && access.token.getUserId(0)) {
|
||||
|
||||
// Create a moment of the expiry expression
|
||||
let expiry = helpers.parseDatePeriod(data.expiry);
|
||||
const expiry = helpers.parseDatePeriod(data.expiry);
|
||||
if (expiry === null) {
|
||||
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
||||
}
|
||||
|
||||
let token_attrs = {
|
||||
id: access.token.getUserId(0)
|
||||
const token_attrs = {
|
||||
id: access.token.getUserId(0),
|
||||
};
|
||||
|
||||
// Only admins can request otherwise scoped tokens
|
||||
@@ -118,17 +113,16 @@ module.exports = {
|
||||
}
|
||||
|
||||
return Token.create({
|
||||
iss: 'api',
|
||||
scope: scope,
|
||||
attrs: token_attrs,
|
||||
expiresIn: data.expiry
|
||||
})
|
||||
.then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString()
|
||||
};
|
||||
});
|
||||
iss: 'api',
|
||||
scope,
|
||||
attrs: token_attrs,
|
||||
expiresIn: data.expiry,
|
||||
}).then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString(),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
throw new error.AssertionFailedError('Existing token contained invalid user data');
|
||||
}
|
||||
@@ -140,23 +134,22 @@ module.exports = {
|
||||
*/
|
||||
getTokenFromUser: (user) => {
|
||||
const expire = '1d';
|
||||
const Token = new TokenModel();
|
||||
const Token = new TokenModel();
|
||||
const expiry = helpers.parseDatePeriod(expire);
|
||||
|
||||
return Token.create({
|
||||
iss: 'api',
|
||||
iss: 'api',
|
||||
attrs: {
|
||||
id: user.id
|
||||
id: user.id,
|
||||
},
|
||||
scope: ['user'],
|
||||
expiresIn: expire
|
||||
})
|
||||
.then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString(),
|
||||
user: user
|
||||
};
|
||||
});
|
||||
}
|
||||
scope: ['user'],
|
||||
expiresIn: expire,
|
||||
}).then((signed) => {
|
||||
return {
|
||||
token: signed.token,
|
||||
expires: expiry.toISOString(),
|
||||
user,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@@ -1,43 +1,40 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const userModel = require('../models/user');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const userModel = require('../models/user');
|
||||
const userPermissionModel = require('../models/user_permission');
|
||||
const authModel = require('../models/auth');
|
||||
const gravatar = require('gravatar');
|
||||
const internalToken = require('./token');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const authModel = require('../models/auth');
|
||||
const gravatar = require('gravatar');
|
||||
const internalToken = require('./token');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
|
||||
function omissions () {
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalUser = {
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
let auth = data.auth || null;
|
||||
const auth = data.auth || null;
|
||||
delete data.auth;
|
||||
|
||||
data.avatar = data.avatar || '';
|
||||
data.roles = data.roles || [];
|
||||
data.roles = data.roles || [];
|
||||
|
||||
if (typeof data.is_disabled !== 'undefined') {
|
||||
data.is_disabled = data.is_disabled ? 1 : 0;
|
||||
}
|
||||
|
||||
return access.can('users:create', data)
|
||||
return access
|
||||
.can('users:create', data)
|
||||
.then(() => {
|
||||
data.avatar = gravatar.url(data.email, {default: 'mm'});
|
||||
data.avatar = gravatar.url(data.email, { default: 'mm' });
|
||||
|
||||
return userModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return userModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((user) => {
|
||||
if (auth) {
|
||||
@@ -45,9 +42,9 @@ const internalUser = {
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
type: auth.type,
|
||||
secret: auth.secret,
|
||||
meta: {}
|
||||
type: auth.type,
|
||||
secret: auth.secret,
|
||||
meta: {},
|
||||
})
|
||||
.then(() => {
|
||||
return user;
|
||||
@@ -58,32 +55,33 @@ const internalUser = {
|
||||
})
|
||||
.then((user) => {
|
||||
// Create permissions row as well
|
||||
let is_admin = data.roles.indexOf('admin') !== -1;
|
||||
const is_admin = data.roles.indexOf('admin') !== -1;
|
||||
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
visibility: is_admin ? 'all' : 'user',
|
||||
proxy_hosts: 'manage',
|
||||
user_id: user.id,
|
||||
visibility: is_admin ? 'all' : 'user',
|
||||
proxy_hosts: 'manage',
|
||||
redirection_hosts: 'manage',
|
||||
dead_hosts: 'manage',
|
||||
streams: 'manage',
|
||||
access_lists: 'manage',
|
||||
certificates: 'manage'
|
||||
dead_hosts: 'manage',
|
||||
streams: 'manage',
|
||||
access_lists: 'manage',
|
||||
certificates: 'manage',
|
||||
})
|
||||
.then(() => {
|
||||
return internalUser.get(access, {id: user.id, expand: ['permissions']});
|
||||
return internalUser.get(access, { id: user.id, expand: ['permissions'] });
|
||||
});
|
||||
})
|
||||
.then((user) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: user
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'created',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: user,
|
||||
})
|
||||
.then(() => {
|
||||
return user;
|
||||
});
|
||||
@@ -103,33 +101,30 @@ const internalUser = {
|
||||
data.is_disabled = data.is_disabled ? 1 : 0;
|
||||
}
|
||||
|
||||
return access.can('users:update', data.id)
|
||||
return access
|
||||
.can('users:update', data.id)
|
||||
.then(() => {
|
||||
|
||||
// Make sure that the user being updated doesn't change their email to another user that is already using it
|
||||
// 1. get user we want to update
|
||||
return internalUser.get(access, {id: data.id})
|
||||
.then((user) => {
|
||||
return internalUser.get(access, { id: data.id }).then((user) => {
|
||||
// 2. if email is to be changed, find other users with that email
|
||||
if (typeof data.email !== 'undefined') {
|
||||
data.email = data.email.toLowerCase().trim();
|
||||
|
||||
// 2. if email is to be changed, find other users with that email
|
||||
if (typeof data.email !== 'undefined') {
|
||||
data.email = data.email.toLowerCase().trim();
|
||||
if (user.email !== data.email) {
|
||||
return internalUser.isEmailAvailable(data.email, data.id).then((available) => {
|
||||
if (!available) {
|
||||
throw new error.ValidationError('Email address already in use - ' + data.email);
|
||||
}
|
||||
|
||||
if (user.email !== data.email) {
|
||||
return internalUser.isEmailAvailable(data.email, data.id)
|
||||
.then((available) => {
|
||||
if (!available) {
|
||||
throw new error.ValidationError('Email address already in use - ' + data.email);
|
||||
}
|
||||
|
||||
return user;
|
||||
});
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// No change to email:
|
||||
return user;
|
||||
});
|
||||
// No change to email:
|
||||
return user;
|
||||
});
|
||||
})
|
||||
.then((user) => {
|
||||
if (user.id !== data.id) {
|
||||
@@ -137,24 +132,22 @@ const internalUser = {
|
||||
throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
|
||||
}
|
||||
|
||||
data.avatar = gravatar.url(data.email || user.email, {default: 'mm'});
|
||||
data.avatar = gravatar.url(data.email || user.email, { default: 'mm' });
|
||||
|
||||
return userModel
|
||||
.query()
|
||||
.patchAndFetchById(user.id, data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
return userModel.query().patchAndFetchById(user.id, data).then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then(() => {
|
||||
return internalUser.get(access, {id: data.id});
|
||||
return internalUser.get(access, { id: data.id });
|
||||
})
|
||||
.then((user) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: data
|
||||
})
|
||||
return internalAuditLog
|
||||
.add(access, {
|
||||
action: 'updated',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: data,
|
||||
})
|
||||
.then(() => {
|
||||
return user;
|
||||
});
|
||||
@@ -178,14 +171,10 @@ const internalUser = {
|
||||
data.id = access.token.getUserId(0);
|
||||
}
|
||||
|
||||
return access.can('users:get', data.id)
|
||||
return access
|
||||
.can('users:get', data.id)
|
||||
.then(() => {
|
||||
let query = userModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[permissions]')
|
||||
.first();
|
||||
const query = userModel.query().where('is_deleted', 0).andWhere('id', data.id).allowGraph('[permissions]').first();
|
||||
|
||||
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||
query.withGraphFetched('[' + data.expand.join(', ') + ']');
|
||||
@@ -213,20 +202,15 @@ const internalUser = {
|
||||
* @param user_id
|
||||
*/
|
||||
isEmailAvailable: (email, user_id) => {
|
||||
let query = userModel
|
||||
.query()
|
||||
.where('email', '=', email.toLowerCase().trim())
|
||||
.where('is_deleted', 0)
|
||||
.first();
|
||||
const query = userModel.query().where('email', '=', email.toLowerCase().trim()).where('is_deleted', 0).first();
|
||||
|
||||
if (typeof user_id !== 'undefined') {
|
||||
query.where('id', '!=', user_id);
|
||||
}
|
||||
|
||||
return query
|
||||
.then((user) => {
|
||||
return !user;
|
||||
});
|
||||
return query.then((user) => {
|
||||
return !user;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -237,9 +221,10 @@ const internalUser = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: (access, data) => {
|
||||
return access.can('users:delete', data.id)
|
||||
return access
|
||||
.can('users:delete', data.id)
|
||||
.then(() => {
|
||||
return internalUser.get(access, {id: data.id});
|
||||
return internalUser.get(access, { id: data.id });
|
||||
})
|
||||
.then((user) => {
|
||||
if (!user) {
|
||||
@@ -255,15 +240,15 @@ const internalUser = {
|
||||
.query()
|
||||
.where('id', user.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
is_deleted: 1,
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'deleted',
|
||||
action: 'deleted',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: _.omit(user, omissions())
|
||||
object_id: user.id,
|
||||
meta: _.omit(user, omissions()),
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -280,19 +265,15 @@ const internalUser = {
|
||||
* @returns {*}
|
||||
*/
|
||||
getCount: (access, search_query) => {
|
||||
return access.can('users:list')
|
||||
return access
|
||||
.can('users:list')
|
||||
.then(() => {
|
||||
let query = userModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0)
|
||||
.first();
|
||||
const query = userModel.query().count('id as count').where('is_deleted', 0).first();
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('user.name', 'like', '%' + search_query + '%')
|
||||
.orWhere('user.email', 'like', '%' + search_query + '%');
|
||||
this.where('user.name', 'like', '%' + search_query + '%').orWhere('user.email', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -312,29 +293,22 @@ const internalUser = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('users:list')
|
||||
.then(() => {
|
||||
let query = userModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[permissions]')
|
||||
.orderBy('name', 'ASC');
|
||||
return access.can('users:list').then(() => {
|
||||
const query = userModel.query().where('is_deleted', 0).groupBy('id').allowGraph('[permissions]').orderBy('name', 'ASC');
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('name', 'like', '%' + search_query + '%')
|
||||
.orWhere('email', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('name', 'like', '%' + search_query + '%').orWhere('email', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -361,9 +335,10 @@ const internalUser = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
setPassword: (access, data) => {
|
||||
return access.can('users:password', data.id)
|
||||
return access
|
||||
.can('users:password', data.id)
|
||||
.then(() => {
|
||||
return internalUser.get(access, {id: data.id});
|
||||
return internalUser.get(access, { id: data.id });
|
||||
})
|
||||
.then((user) => {
|
||||
if (user.id !== data.id) {
|
||||
@@ -377,10 +352,11 @@ const internalUser = {
|
||||
throw new error.ValidationError('Current password was not supplied');
|
||||
}
|
||||
|
||||
return internalToken.getTokenFromEmail({
|
||||
identity: user.email,
|
||||
secret: data.current
|
||||
})
|
||||
return internalToken
|
||||
.getTokenFromEmail({
|
||||
identity: user.email,
|
||||
secret: data.current,
|
||||
})
|
||||
.then(() => {
|
||||
return user;
|
||||
});
|
||||
@@ -398,37 +374,31 @@ const internalUser = {
|
||||
.then((existing_auth) => {
|
||||
if (existing_auth) {
|
||||
// patch
|
||||
return authModel
|
||||
.query()
|
||||
.where('user_id', user.id)
|
||||
.andWhere('type', data.type)
|
||||
.patch({
|
||||
type: data.type, // This is required for the model to encrypt on save
|
||||
secret: data.secret
|
||||
});
|
||||
return authModel.query().where('user_id', user.id).andWhere('type', data.type).patch({
|
||||
type: data.type, // This is required for the model to encrypt on save
|
||||
secret: data.secret,
|
||||
});
|
||||
} else {
|
||||
// insert
|
||||
return authModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
type: data.type,
|
||||
secret: data.secret,
|
||||
meta: {}
|
||||
});
|
||||
return authModel.query().insert({
|
||||
user_id: user.id,
|
||||
type: data.type,
|
||||
secret: data.secret,
|
||||
meta: {},
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// Add to Audit Log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
action: 'updated',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: {
|
||||
name: user.name,
|
||||
object_id: user.id,
|
||||
meta: {
|
||||
name: user.name,
|
||||
password_changed: true,
|
||||
auth_type: data.type
|
||||
}
|
||||
auth_type: data.type,
|
||||
},
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -443,9 +413,10 @@ const internalUser = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
setPermissions: (access, data) => {
|
||||
return access.can('users:permissions', data.id)
|
||||
return access
|
||||
.can('users:permissions', data.id)
|
||||
.then(() => {
|
||||
return internalUser.get(access, {id: data.id});
|
||||
return internalUser.get(access, { id: data.id });
|
||||
})
|
||||
.then((user) => {
|
||||
if (user.id !== data.id) {
|
||||
@@ -467,26 +438,23 @@ const internalUser = {
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.where('user_id', user.id)
|
||||
.patchAndFetchById(existing_auth.id, _.assign({user_id: user.id}, data));
|
||||
.patchAndFetchById(existing_auth.id, _.assign({ user_id: user.id }, data));
|
||||
} else {
|
||||
// insert
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.insertAndFetch(_.assign({user_id: user.id}, data));
|
||||
return userPermissionModel.query().insertAndFetch(_.assign({ user_id: user.id }, data));
|
||||
}
|
||||
})
|
||||
.then((permissions) => {
|
||||
// Add to Audit Log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'updated',
|
||||
action: 'updated',
|
||||
object_type: 'user',
|
||||
object_id: user.id,
|
||||
meta: {
|
||||
name: user.name,
|
||||
permissions: permissions
|
||||
}
|
||||
object_id: user.id,
|
||||
meta: {
|
||||
name: user.name,
|
||||
permissions,
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -500,14 +468,15 @@ const internalUser = {
|
||||
* @param {Integer} data.id
|
||||
*/
|
||||
loginAs: (access, data) => {
|
||||
return access.can('users:loginas', data.id)
|
||||
return access
|
||||
.can('users:loginas', data.id)
|
||||
.then(() => {
|
||||
return internalUser.get(access, data);
|
||||
})
|
||||
.then((user) => {
|
||||
return internalToken.getTokenFromUser(user);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = internalUser;
|
||||
|
Reference in New Issue
Block a user