Skip to content

Commit

Permalink
Add certificate to streams database model
Browse files Browse the repository at this point in the history
  • Loading branch information
jbowring committed Jun 2, 2024
1 parent 1d3d5be commit 7307515
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 17 deletions.
97 changes: 85 additions & 12 deletions backend/internal/stream.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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');
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');
const internalCertificate = require('./certificate');
const internalHost = require('./host');

function omissions () {
return ['is_deleted'];
Expand All @@ -17,6 +19,12 @@ const internalStream = {
* @returns {Promise}
*/
create: (access, data) => {
let create_certificate = data.certificate_id === 'new';

if (create_certificate) {
delete data.certificate_id;
}

return access.can('streams:create', data)
.then((/*access_data*/) => {
// TODO: At this point the existing ports should have been checked
Expand All @@ -26,11 +34,40 @@ const internalStream = {
data.meta = {};
}

let data_no_domains = structuredClone(data);

// streams aren't routed by domain name so don't store domain names in the DB
delete data_no_domains.domain_names;

return streamModel
.query()
.insertAndFetch(data)
.insertAndFetch(data_no_domains)
.then(utils.omitRow(omissions()));
})
.then((row) => {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, data)
.then((cert) => {
// update host with cert id
return internalStream.update(access, {
id: row.id,
certificate_id: cert.id
});
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then((row) => {
// re-fetch with cert
return internalStream.get(access, {
id: row.id,
expand: ['certificate', 'owner']
});
})
.then((row) => {
// Configure nginx
return internalNginx.configure(streamModel, 'stream', row)
Expand Down Expand Up @@ -59,6 +96,12 @@ const internalStream = {
* @return {Promise}
*/
update: (access, data) => {
let create_certificate = data.certificate_id === 'new';

if (create_certificate) {
delete data.certificate_id;
}

return access.can('streams:update', data.id)
.then((/*access_data*/) => {
// TODO: at this point the existing streams should have been checked
Expand All @@ -70,6 +113,28 @@ const internalStream = {
throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}

if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then((cert) => {
// update host with cert id
data.certificate_id = cert.id;
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then((row) => {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, {
domain_names: row.domain_names
}, data);

return streamModel
.query()
.patchAndFetchById(row.id, data)
Expand Down Expand Up @@ -114,7 +179,7 @@ const internalStream = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner]')
.allowGraph('[owner,certificate]')
.first();

if (access_data.permission_visibility !== 'all') {
Expand All @@ -131,6 +196,7 @@ const internalStream = {
if (!row) {
throw new error.ItemNotFoundError(data.id);
}
row = internalHost.cleanRowCertificateMeta(row);
// Custom omissions
if (typeof data.omit !== 'undefined' && data.omit !== null) {
row = _.omit(row, data.omit);
Expand Down Expand Up @@ -196,14 +262,14 @@ const internalStream = {
.then(() => {
return internalStream.get(access, {
id: data.id,
expand: ['owner']
expand: ['certificate', 'owner']
});
})
.then((row) => {
if (!row) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Host is already enabled');
throw new error.ValidationError('Stream is already enabled');
}

row.enabled = 1;
Expand Down Expand Up @@ -249,7 +315,7 @@ const internalStream = {
if (!row) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Host is already disabled');
throw new error.ValidationError('Stream is already disabled');
}

row.enabled = 0;
Expand Down Expand Up @@ -297,7 +363,7 @@ const internalStream = {
.query()
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner]')
.allowGraph('[owner,certificate]')
.orderBy('incoming_port', 'ASC');

if (access_data.permission_visibility !== 'all') {
Expand All @@ -316,6 +382,13 @@ const internalStream = {
}

return query.then(utils.omitRows(omissions()));
})
.then((rows) => {
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
return internalHost.cleanAllRowsCertificateMeta(rows);
}

return rows;
});
},

Expand Down
38 changes: 38 additions & 0 deletions backend/migrations/20240427161436_stream_ssl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const migrate_name = 'stream_ssl';
const logger = require('../logger').migrate;

/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info('[' + migrate_name + '] Migrating Up...');

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

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

return knex.schema.table('stream', (table) => {
table.dropColumn('certificate_id');
})
.then(function () {
logger.info('[' + migrate_name + '] stream Table altered');
});
};
20 changes: 16 additions & 4 deletions backend/models/stream.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 now = require('./now_helper');
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
const Certificate = require('./certificate');

Model.knex(db);

Expand Down Expand Up @@ -47,6 +48,17 @@ class Stream extends Model {
modify: function (qb) {
qb.where('user.is_deleted', 0);
}
},
certificate: {
relation: Model.HasOneRelation,
modelClass: Certificate,
join: {
from: 'stream.certificate_id',
to: 'certificate.id'
},
modify: function (qb) {
qb.where('certificate.is_deleted', 0);
}
}
};
}
Expand Down
24 changes: 24 additions & 0 deletions backend/schema/endpoints/streams.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
"udp_forwarding": {
"type": "boolean"
},
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"certificate_id": {
"$ref": "../definitions.json#/definitions/certificate_id"
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
Expand Down Expand Up @@ -78,6 +84,12 @@
"udp_forwarding": {
"$ref": "#/definitions/udp_forwarding"
},
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
Expand Down Expand Up @@ -137,6 +149,12 @@
"udp_forwarding": {
"$ref": "#/definitions/udp_forwarding"
},
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"meta": {
"$ref": "#/definitions/meta"
}
Expand Down Expand Up @@ -177,6 +195,12 @@
"udp_forwarding": {
"$ref": "#/definitions/udp_forwarding"
},
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"certificate_id": {
"$ref": "#/definitions/certificate_id"
},
"meta": {
"$ref": "#/definitions/meta"
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/js/app/nginx/stream/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ module.exports = Mn.View.extend({
onRender: function () {
let view = this;

view.fetch(['owner'])
view.fetch(['owner', 'certificate'])
.then(response => {
if (!view.isDestroyed()) {
if (response && response.length) {
Expand Down

0 comments on commit 7307515

Please sign in to comment.