Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions backend/internal/proxy-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ const internalProxyHost = {
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);

// If upstream is used, clear forwarding fields
if (data.upstream_id) {
data.forward_host = '';
data.forward_port = 0;
}

// This is a UI-only field, remove it
delete data.forward_to_type;

// Fix for db field not having a default value
// for this optional field.
if (typeof data.advanced_config === 'undefined') {
Expand Down Expand Up @@ -81,7 +90,7 @@ const internalProxyHost = {
// re-fetch with cert
return internalProxyHost.get(access, {
id: row.id,
expand: ['certificate', 'owner', 'access_list.[clients,items]']
expand: ['certificate', 'owner', 'access_list.[clients,items]', 'upstream']
});
})
.then((row) => {
Expand Down Expand Up @@ -174,6 +183,18 @@ const internalProxyHost = {

data = internalHost.cleanSslHstsData(data, row);

// If upstream is used, clear forwarding fields
if (data.upstream_id) {
data.forward_host = '';
data.forward_port = 0;
} else if (data.upstream_id === 0) {
// Upstream was removed, make sure it's cleared
data.upstream_id = 0;
}

// This is a UI-only field, remove it
delete data.forward_to_type;

return proxyHostModel
.query()
.where({id: data.id})
Expand All @@ -195,7 +216,7 @@ const internalProxyHost = {
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['owner', 'certificate', 'access_list.[clients,items]']
expand: ['owner', 'certificate', 'access_list.[clients,items]', 'upstream']
})
.then((row) => {
if (!row.enabled) {
Expand Down Expand Up @@ -232,7 +253,7 @@ const internalProxyHost = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner,access_list.[clients,items],certificate]')
.allowGraph('[owner,access_list.[clients,items],certificate,upstream]')
.first();

if (access_data.permission_visibility !== 'all') {
Expand Down Expand Up @@ -315,7 +336,7 @@ const internalProxyHost = {
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['certificate', 'owner', 'access_list']
expand: ['certificate', 'owner', 'access_list', 'upstream']
});
})
.then((row) => {
Expand Down Expand Up @@ -416,7 +437,7 @@ const internalProxyHost = {
.query()
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner,access_list,certificate]')
.allowGraph('[owner,access_list,certificate,upstream]')
.orderBy(castJsonIfNeed('domain_names'), 'ASC');

if (access_data.permission_visibility !== 'all') {
Expand Down
175 changes: 175 additions & 0 deletions backend/internal/upstream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
const error = require('../lib/error');
const utils = require('../lib/utils');
const upstreamModel = require('../models/upstream');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const proxyHostModel = require('../models/proxy_host');

function omissions() {
return ['is_deleted'];
}

const internalUpstream = {

/**
* @param {Access} access
* @param {Object} data
* @returns {Promise}
*/
create: (access, data) => {
return access.can('upstreams:create', data)
.then(() => {
data.owner_user_id = access.token.getUserId(1);
return upstreamModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
})
.then((row) => {
// Audit log
return internalAuditLog.add(access, {
action: 'created',
object_type: 'upstream',
object_id: row.id,
meta: data
}).then(() => row);
});
},

/**
* @param {Access} access
* @param {Object} data
* @return {Promise}
*/
update: (access, data) => {
return access.can('upstreams:update', data.id)
.then(() => {
return internalUpstream.get(access, { id: data.id });
})
.then((row) => {
if (row.id !== data.id) {
throw new error.InternalValidationError('Upstream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}

return upstreamModel.query().patchAndFetchById(row.id, data).then(utils.omitRow(omissions()));
})
.then((row) => {
// Audit log
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'upstream',
object_id: row.id,
meta: data
}).then(() => row);
})
.then((row) => {
// Find all proxy hosts using this upstream and re-generate their configs
return proxyHostModel.query()
.where('upstream_id', row.id)
.andWhere('is_deleted', 0)
.withGraphFetched('[certificate,access_list,upstream]') // The fix is here: use withGraphFetched
.then((hosts) => {
if (hosts && hosts.length) {
return internalNginx.bulkGenerateConfigs('proxy_host', hosts)
.then(internalNginx.reload)
.then(() => row);
}
return row;
});
});
},

/**
* @param {Access} access
* @param {Object} data
* @return {Promise}
*/
get: (access, data) => {
if (typeof data === 'undefined') {
data = {};
}

return access.can('upstreams:get', data.id)
.then((access_data) => {
let query = upstreamModel
.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));
}

if (typeof data.expand !== 'undefined' && data.expand !== null) {
query.withGraphFetched(`[${data.expand.join(', ')}]`);
}

return query.then(utils.omitRow(omissions()));
})
.then((row) => {
if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
return row;
});
},

/**
* @param {Access} access
* @param {Object} data
* @returns {Promise}
*/
delete: (access, data) => {
return access.can('upstreams:delete', data.id)
.then(() => {
return internalUpstream.get(access, { id: data.id });
})
.then((row) => {
if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}

return upstreamModel.query()
.where('id', row.id)
.patch({ is_deleted: 1 });
})
.then(() => {
return internalAuditLog.add(access, {
action: 'deleted',
object_type: 'upstream',
object_id: data.id
});
})
.then(() => true);
},

/**
* @param {Access} access
* @returns {Promise}
*/
getAll: (access, expand, search_query) => {
return access.can('upstreams:list')
.then((access_data) => {
let query = upstreamModel
.query()
.where('is_deleted', 0)
.allowGraph('[owner]')
.orderBy('name', 'ASC');

if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}

if (typeof search_query === 'string' && search_query) {
query.where('name', 'like', `%${search_query}%`);
}

if (typeof expand !== 'undefined' && expand !== null) {
query.withGraphFetched(`[${expand.join(', ')}]`);
}

return query.then(utils.omitRows(omissions()));
});
}
};

module.exports = internalUpstream;
3 changes: 2 additions & 1 deletion backend/internal/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const internalUser = {
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage'
certificates: 'manage',
upstreams: 'manage'
})
.then(() => {
return internalUser.get(access, {id: user.id, expand: ['permissions']});
Expand Down
3 changes: 2 additions & 1 deletion backend/lib/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ module.exports = function (token_string) {
permission_dead_hosts: permissions.dead_hosts,
permission_streams: permissions.streams,
permission_access_lists: permissions.access_lists,
permission_certificates: permissions.certificates
permission_certificates: permissions.certificates,
permission_upstreams: permissions.upstreams
}
};

Expand Down
28 changes: 28 additions & 0 deletions backend/lib/access/upstreams-create.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": [
"permission_upstreams",
"roles"
],
"properties": {
"permission_upstreams": {
"$ref": "perms#/definitions/manage"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user"
]
}
}
}
}
]
}
28 changes: 28 additions & 0 deletions backend/lib/access/upstreams-delete.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": [
"permission_upstreams",
"roles"
],
"properties": {
"permission_upstreams": {
"$ref": "perms#/definitions/manage"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user"
]
}
}
}
}
]
}
28 changes: 28 additions & 0 deletions backend/lib/access/upstreams-get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": [
"permission_upstreams",
"roles"
],
"properties": {
"permission_upstreams": {
"$ref": "perms#/definitions/view"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user"
]
}
}
}
}
]
}
28 changes: 28 additions & 0 deletions backend/lib/access/upstreams-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": [
"permission_upstreams",
"roles"
],
"properties": {
"permission_upstreams": {
"$ref": "perms#/definitions/view"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user"
]
}
}
}
}
]
}
Loading