Skip to content

Client Access Lists #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 14, 2020
Merged
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
90 changes: 70 additions & 20 deletions backend/internal/access-list.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
const _ = require('lodash');
const fs = require('fs');
const batchflow = require('batchflow');
const logger = require('../logger').access;
const error = require('../lib/error');
const accessListModel = require('../models/access_list');
const accessListAuthModel = require('../models/access_list_auth');
const proxyHostModel = require('../models/proxy_host');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const utils = require('../lib/utils');
const _ = require('lodash');
const fs = require('fs');
const batchflow = require('batchflow');
const logger = require('../logger').access;
const error = require('../lib/error');
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 utils = require('../lib/utils');

function omissions () {
return ['is_deleted'];
Expand All @@ -29,14 +30,16 @@ const internalAccessList = {
.omit(omissions())
.insertAndFetch({
name: data.name,
satify_any: data.satify_any,
owner_user_id: access.token.getUserId(1)
});
})
.then((row) => {
data.id = row.id;

// Now add the items
let promises = [];

// Now add the items
data.items.map((item) => {
promises.push(accessListAuthModel
.query()
Expand All @@ -48,13 +51,27 @@ const internalAccessList = {
);
});

// Now add the clients
if (typeof data.clients !== 'undefined' && data.clients) {
data.clients.map((client) => {
promises.push(accessListClientModel
.query()
.insert({
access_list_id: row.id,
address: client.address,
directive: client.directive
})
);
});
}

return Promise.all(promises);
})
.then(() => {
// re-fetch with expansions
return internalAccessList.get(access, {
id: data.id,
expand: ['owner', 'items']
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.clients']
}, true /* <- skip masking */);
})
.then((row) => {
Expand All @@ -64,7 +81,7 @@ const internalAccessList = {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
return internalNginx.reload();
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
.then(() => {
Expand Down Expand Up @@ -109,7 +126,8 @@ const internalAccessList = {
.query()
.where({id: data.id})
.patch({
name: data.name
name: data.name,
satify_any: data.satify_any,
});
}
})
Expand Down Expand Up @@ -153,6 +171,38 @@ const internalAccessList = {
});
}
})
.then(() => {
// Check for clients and add/update/remove them
if (typeof data.clients !== 'undefined' && data.clients) {
let promises = [];

data.clients.map(function (client) {
if (client.address) {
promises.push(accessListClientModel
.query()
.insert({
access_list_id: data.id,
address: client.address,
directive: client.directive
})
);
}
});

let query = accessListClientModel
.query()
.delete()
.where('access_list_id', data.id);

return query
.then(() => {
// Add new items
if (promises.length) {
return Promise.all(promises);
}
});
}
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
Expand All @@ -166,14 +216,14 @@ const internalAccessList = {
// re-fetch with expansions
return internalAccessList.get(access, {
id: data.id,
expand: ['owner', 'items']
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.clients']
}, true /* <- skip masking */);
})
.then((row) => {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
return internalNginx.reload();
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
.then(() => {
Expand Down Expand Up @@ -204,7 +254,7 @@ const internalAccessList = {
.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)
.allowEager('[owner,items,proxy_hosts]')
.allowEager('[owner,items,clients,proxy_hosts,proxy_hosts.access_list.clients]')
.omit(['access_list.is_deleted'])
.first();

Expand Down Expand Up @@ -246,7 +296,7 @@ const internalAccessList = {
delete: (access, data) => {
return access.can('access_lists:delete', data.id)
.then(() => {
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items']});
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']});
})
.then((row) => {
if (!row) {
Expand Down Expand Up @@ -330,7 +380,7 @@ const internalAccessList = {
.where('access_list.is_deleted', 0)
.groupBy('access_list.id')
.omit(['access_list.is_deleted'])
.allowEager('[owner,items]')
.allowEager('[owner,items,clients]')
.orderBy('access_list.name', 'ASC');

if (access_data.permission_visibility !== 'all') {
Expand Down
6 changes: 3 additions & 3 deletions backend/internal/proxy-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const internalProxyHost = {
// re-fetch with cert
return internalProxyHost.get(access, {
id: row.id,
expand: ['certificate', 'owner', 'access_list']
expand: ['certificate', 'owner', 'access_list.clients']
});
})
.then((row) => {
Expand Down Expand Up @@ -186,7 +186,7 @@ const internalProxyHost = {
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['owner', 'certificate', 'access_list']
expand: ['owner', 'certificate', 'access_list.clients']
})
.then((row) => {
// Configure nginx
Expand Down Expand Up @@ -219,7 +219,7 @@ const internalProxyHost = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowEager('[owner,access_list,certificate]')
.allowEager('[owner,access_list,access_list.clients,certificate]')
.first();

if (access_data.permission_visibility !== 'all') {
Expand Down
53 changes: 53 additions & 0 deletions backend/migrations/20200410143839_access_list_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const migrate_name = 'access_list_client';
const logger = require('../logger').migrate;

/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex/*, Promise*/) {

logger.info('[' + migrate_name + '] Migrating Up...');

return knex.schema.createTable('access_list_client', (table) => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('access_list_id').notNull().unsigned();
table.string('address').notNull();
table.string('directive').notNull();
table.json('meta').notNull();

})
.then(function () {
logger.info('[' + migrate_name + '] access_list_client Table created');

return knex.schema.table('access_list', function (access_list) {
access_list.integer('satify_any').notNull().defaultTo(0);
});
})
.then(() => {
logger.info('[' + migrate_name + '] access_list Table altered');
});
};

/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Down...');

return knex.schema.dropTable('access_list_client')
.then(() => {
logger.info('[' + migrate_name + '] access_list_client Table dropped');
});
};
24 changes: 20 additions & 4 deletions backend/models/access_list.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Objection Docs:
// http://vincit.github.io/objection.js/

const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const AccessListAuth = require('./access_list_auth');
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const AccessListAuth = require('./access_list_auth');
const AccessListClient = require('./access_list_client');

Model.knex(db);

Expand Down Expand Up @@ -62,6 +63,17 @@ class AccessList extends Model {
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
}
},
clients: {
relation: Model.HasManyRelation,
modelClass: AccessListClient,
join: {
from: 'access_list.id',
to: 'access_list_client.access_list_id'
},
modify: function (qb) {
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
}
},
proxy_hosts: {
relation: Model.HasManyRelation,
modelClass: ProxyHost,
Expand All @@ -76,6 +88,10 @@ class AccessList extends Model {
}
};
}

get satisfy() {
return this.satify_any ? 'satisfy any' : 'satisfy all';
}
}

module.exports = AccessList;
58 changes: 58 additions & 0 deletions backend/models/access_list_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Objection Docs:
// http://vincit.github.io/objection.js/

const db = require('../db');
const Model = require('objection').Model;

Model.knex(db);

class AccessListClient extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');

// Default for meta
if (typeof this.meta === 'undefined') {
this.meta = {};
}
}

$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
}

static get name () {
return 'AccessListClient';
}

static get tableName () {
return 'access_list_client';
}

static get jsonAttributes () {
return ['meta'];
}

static get relationMappings () {
return {
access_list: {
relation: Model.HasOneRelation,
modelClass: require('./access_list'),
join: {
from: 'access_list_client.access_list_id',
to: 'access_list.id'
},
modify: function (qb) {
qb.where('access_list.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']);
}
}
};
}

get rule() {
return `${this.directive} ${this.address}`;
}
}

module.exports = AccessListClient;
Loading