Skip to content

Commit 4283677

Browse files
authoredFeb 5, 2025
Merge branch 'develop' into feature/add-zone-edit-certbot-plugin
2 parents c05f969 + 2a07544 commit 4283677

39 files changed

+921
-211
lines changed
 

‎backend/internal/access-list.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ const internalAccessList = {
258258
})
259259
.where('access_list.is_deleted', 0)
260260
.andWhere('access_list.id', data.id)
261+
.groupBy('access_list.id')
261262
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
262263
.first();
263264

@@ -507,8 +508,13 @@ const internalAccessList = {
507508
if (typeof item.password !== 'undefined' && item.password.length) {
508509
logger.info('Adding: ' + item.username);
509510

510-
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
511-
.then((/*result*/) => {
511+
utils.execFile('openssl', ['passwd', '-apr1', item.password])
512+
.then((res) => {
513+
try {
514+
fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
515+
} catch (err) {
516+
reject(err);
517+
}
512518
next();
513519
})
514520
.catch((err) => {

‎backend/internal/certificate.js

+6
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ const internalCertificate = {
313313
.where('is_deleted', 0)
314314
.andWhere('id', data.id)
315315
.allowGraph('[owner]')
316+
.allowGraph('[proxy_hosts]')
317+
.allowGraph('[redirection_hosts]')
318+
.allowGraph('[dead_hosts]')
316319
.first();
317320

318321
if (access_data.permission_visibility !== 'all') {
@@ -464,6 +467,9 @@ const internalCertificate = {
464467
.where('is_deleted', 0)
465468
.groupBy('id')
466469
.allowGraph('[owner]')
470+
.allowGraph('[proxy_hosts]')
471+
.allowGraph('[redirection_hosts]')
472+
.allowGraph('[dead_hosts]')
467473
.orderBy('nice_name', 'ASC');
468474

469475
if (access_data.permission_visibility !== 'all') {

‎backend/internal/stream.js

+98-21
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
const _ = require('lodash');
2-
const error = require('../lib/error');
3-
const utils = require('../lib/utils');
4-
const streamModel = require('../models/stream');
5-
const internalNginx = require('./nginx');
6-
const internalAuditLog = require('./audit-log');
7-
const {castJsonIfNeed} = require('../lib/helpers');
1+
const _ = require('lodash');
2+
const error = require('../lib/error');
3+
const utils = require('../lib/utils');
4+
const streamModel = require('../models/stream');
5+
const internalNginx = require('./nginx');
6+
const internalAuditLog = require('./audit-log');
7+
const internalCertificate = require('./certificate');
8+
const internalHost = require('./host');
9+
const {castJsonIfNeed} = require('../lib/helpers');
810

911
function omissions () {
10-
return ['is_deleted'];
12+
return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
1113
}
1214

1315
const internalStream = {
@@ -18,6 +20,12 @@ const internalStream = {
1820
* @returns {Promise}
1921
*/
2022
create: (access, data) => {
23+
const create_certificate = data.certificate_id === 'new';
24+
25+
if (create_certificate) {
26+
delete data.certificate_id;
27+
}
28+
2129
return access.can('streams:create', data)
2230
.then((/*access_data*/) => {
2331
// TODO: At this point the existing ports should have been checked
@@ -27,16 +35,44 @@ const internalStream = {
2735
data.meta = {};
2836
}
2937

38+
// streams aren't routed by domain name so don't store domain names in the DB
39+
let data_no_domains = structuredClone(data);
40+
delete data_no_domains.domain_names;
41+
3042
return streamModel
3143
.query()
32-
.insertAndFetch(data)
44+
.insertAndFetch(data_no_domains)
3345
.then(utils.omitRow(omissions()));
3446
})
47+
.then((row) => {
48+
if (create_certificate) {
49+
return internalCertificate.createQuickCertificate(access, data)
50+
.then((cert) => {
51+
// update host with cert id
52+
return internalStream.update(access, {
53+
id: row.id,
54+
certificate_id: cert.id
55+
});
56+
})
57+
.then(() => {
58+
return row;
59+
});
60+
} else {
61+
return row;
62+
}
63+
})
64+
.then((row) => {
65+
// re-fetch with cert
66+
return internalStream.get(access, {
67+
id: row.id,
68+
expand: ['certificate', 'owner']
69+
});
70+
})
3571
.then((row) => {
3672
// Configure nginx
3773
return internalNginx.configure(streamModel, 'stream', row)
3874
.then(() => {
39-
return internalStream.get(access, {id: row.id, expand: ['owner']});
75+
return row;
4076
});
4177
})
4278
.then((row) => {
@@ -60,6 +96,12 @@ const internalStream = {
6096
* @return {Promise}
6197
*/
6298
update: (access, data) => {
99+
const create_certificate = data.certificate_id === 'new';
100+
101+
if (create_certificate) {
102+
delete data.certificate_id;
103+
}
104+
63105
return access.can('streams:update', data.id)
64106
.then((/*access_data*/) => {
65107
// TODO: at this point the existing streams should have been checked
@@ -71,16 +113,32 @@ const internalStream = {
71113
throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
72114
}
73115

116+
if (create_certificate) {
117+
return internalCertificate.createQuickCertificate(access, {
118+
domain_names: data.domain_names || row.domain_names,
119+
meta: _.assign({}, row.meta, data.meta)
120+
})
121+
.then((cert) => {
122+
// update host with cert id
123+
data.certificate_id = cert.id;
124+
})
125+
.then(() => {
126+
return row;
127+
});
128+
} else {
129+
return row;
130+
}
131+
})
132+
.then((row) => {
133+
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
134+
data = _.assign({}, {
135+
domain_names: row.domain_names
136+
}, data);
137+
74138
return streamModel
75139
.query()
76140
.patchAndFetchById(row.id, data)
77141
.then(utils.omitRow(omissions()))
78-
.then((saved_row) => {
79-
return internalNginx.configure(streamModel, 'stream', saved_row)
80-
.then(() => {
81-
return internalStream.get(access, {id: row.id, expand: ['owner']});
82-
});
83-
})
84142
.then((saved_row) => {
85143
// Add to audit log
86144
return internalAuditLog.add(access, {
@@ -93,6 +151,17 @@ const internalStream = {
93151
return saved_row;
94152
});
95153
});
154+
})
155+
.then(() => {
156+
return internalStream.get(access, {id: data.id, expand: ['owner', 'certificate']})
157+
.then((row) => {
158+
return internalNginx.configure(streamModel, 'stream', row)
159+
.then((new_meta) => {
160+
row.meta = new_meta;
161+
row = internalHost.cleanRowCertificateMeta(row);
162+
return _.omit(row, omissions());
163+
});
164+
});
96165
});
97166
},
98167

@@ -115,7 +184,7 @@ const internalStream = {
115184
.query()
116185
.where('is_deleted', 0)
117186
.andWhere('id', data.id)
118-
.allowGraph('[owner]')
187+
.allowGraph('[owner,certificate]')
119188
.first();
120189

121190
if (access_data.permission_visibility !== 'all') {
@@ -132,6 +201,7 @@ const internalStream = {
132201
if (!row || !row.id) {
133202
throw new error.ItemNotFoundError(data.id);
134203
}
204+
row = internalHost.cleanRowCertificateMeta(row);
135205
// Custom omissions
136206
if (typeof data.omit !== 'undefined' && data.omit !== null) {
137207
row = _.omit(row, data.omit);
@@ -197,14 +267,14 @@ const internalStream = {
197267
.then(() => {
198268
return internalStream.get(access, {
199269
id: data.id,
200-
expand: ['owner']
270+
expand: ['certificate', 'owner']
201271
});
202272
})
203273
.then((row) => {
204274
if (!row || !row.id) {
205275
throw new error.ItemNotFoundError(data.id);
206276
} else if (row.enabled) {
207-
throw new error.ValidationError('Host is already enabled');
277+
throw new error.ValidationError('Stream is already enabled');
208278
}
209279

210280
row.enabled = 1;
@@ -250,7 +320,7 @@ const internalStream = {
250320
if (!row || !row.id) {
251321
throw new error.ItemNotFoundError(data.id);
252322
} else if (!row.enabled) {
253-
throw new error.ValidationError('Host is already disabled');
323+
throw new error.ValidationError('Stream is already disabled');
254324
}
255325

256326
row.enabled = 0;
@@ -298,7 +368,7 @@ const internalStream = {
298368
.query()
299369
.where('is_deleted', 0)
300370
.groupBy('id')
301-
.allowGraph('[owner]')
371+
.allowGraph('[owner,certificate]')
302372
.orderByRaw('CAST(incoming_port AS INTEGER) ASC');
303373

304374
if (access_data.permission_visibility !== 'all') {
@@ -317,6 +387,13 @@ const internalStream = {
317387
}
318388

319389
return query.then(utils.omitRows(omissions()));
390+
})
391+
.then((rows) => {
392+
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
393+
return internalHost.cleanAllRowsCertificateMeta(rows);
394+
}
395+
396+
return rows;
320397
});
321398
},
322399

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const migrate_name = 'stream_ssl';
2+
const logger = require('../logger').migrate;
3+
4+
/**
5+
* Migrate
6+
*
7+
* @see http://knexjs.org/#Schema
8+
*
9+
* @param {Object} knex
10+
* @returns {Promise}
11+
*/
12+
exports.up = function (knex) {
13+
logger.info('[' + migrate_name + '] Migrating Up...');
14+
15+
return knex.schema.table('stream', (table) => {
16+
table.integer('certificate_id').notNull().unsigned().defaultTo(0);
17+
})
18+
.then(function () {
19+
logger.info('[' + migrate_name + '] stream Table altered');
20+
});
21+
};
22+
23+
/**
24+
* Undo Migrate
25+
*
26+
* @param {Object} knex
27+
* @returns {Promise}
28+
*/
29+
exports.down = function (knex) {
30+
logger.info('[' + migrate_name + '] Migrating Down...');
31+
32+
return knex.schema.table('stream', (table) => {
33+
table.dropColumn('certificate_id');
34+
})
35+
.then(function () {
36+
logger.info('[' + migrate_name + '] stream Table altered');
37+
});
38+
};

‎backend/models/certificate.js

+38-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
const db = require('../db');
55
const helpers = require('../lib/helpers');
66
const Model = require('objection').Model;
7-
const User = require('./user');
87
const now = require('./now_helper');
98

109
Model.knex(db);
@@ -68,6 +67,11 @@ class Certificate extends Model {
6867
}
6968

7069
static get relationMappings () {
70+
const ProxyHost = require('./proxy_host');
71+
const DeadHost = require('./dead_host');
72+
const User = require('./user');
73+
const RedirectionHost = require('./redirection_host');
74+
7175
return {
7276
owner: {
7377
relation: Model.HasOneRelation,
@@ -79,6 +83,39 @@ class Certificate extends Model {
7983
modify: function (qb) {
8084
qb.where('user.is_deleted', 0);
8185
}
86+
},
87+
proxy_hosts: {
88+
relation: Model.HasManyRelation,
89+
modelClass: ProxyHost,
90+
join: {
91+
from: 'certificate.id',
92+
to: 'proxy_host.certificate_id'
93+
},
94+
modify: function (qb) {
95+
qb.where('proxy_host.is_deleted', 0);
96+
}
97+
},
98+
dead_hosts: {
99+
relation: Model.HasManyRelation,
100+
modelClass: DeadHost,
101+
join: {
102+
from: 'certificate.id',
103+
to: 'dead_host.certificate_id'
104+
},
105+
modify: function (qb) {
106+
qb.where('dead_host.is_deleted', 0);
107+
}
108+
},
109+
redirection_hosts: {
110+
relation: Model.HasManyRelation,
111+
modelClass: RedirectionHost,
112+
join: {
113+
from: 'certificate.id',
114+
to: 'redirection_host.certificate_id'
115+
},
116+
modify: function (qb) {
117+
qb.where('redirection_host.is_deleted', 0);
118+
}
82119
}
83120
};
84121
}

0 commit comments

Comments
 (0)
Failed to load comments.