@xen-orchestra/rest-api 0.29.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -1
- package/dist/abstract-classes/base-controller.mjs +20 -3
- package/dist/abstract-classes/listener.mjs +116 -12
- package/dist/acl-privileges/acl-privilege.controller.mjs +172 -0
- package/dist/acl-roles/acl-role.controller.mjs +384 -0
- package/dist/alarms/alarm.controller.mjs +22 -9
- package/dist/alarms/alarm.service.mjs +8 -0
- package/dist/backup-archives/backup-archive.controller.mjs +30 -21
- package/dist/backup-archives/backup-archive.service.mjs +21 -0
- package/dist/backup-jobs/backup-job.controller.mjs +59 -15
- package/dist/backup-jobs/backup-job.service.mjs +7 -0
- package/dist/backup-logs/backup-log.controller.mjs +25 -11
- package/dist/backup-logs/backup-log.service.mjs +19 -0
- package/dist/backup-repositories/backup-repositories.controller.mjs +21 -3
- package/dist/events/event.class.mjs +24 -9
- package/dist/events/event.controller.mjs +3 -0
- package/dist/events/event.service.mjs +2 -1
- package/dist/groups/group.controller.mjs +90 -6
- package/dist/hosts/host.controller.mjs +78 -7
- package/dist/ioc/ioc.mjs +13 -4
- package/dist/messages/message.controller.mjs +29 -8
- package/dist/middlewares/acl.middleware.mjs +206 -0
- package/dist/middlewares/authentication.middleware.mjs +15 -6
- package/dist/middlewares/tsoa-to-xo-error.middleware.mjs +19 -1
- package/dist/networks/network.controller.mjs +60 -9
- package/dist/open-api/oa-examples/acl-privilege.oa-example.mjs +25 -0
- package/dist/open-api/oa-examples/acl-role.oa-example.mjs +22 -0
- package/dist/open-api/oa-examples/backup-archive.oa-example.mjs +6 -6
- package/dist/open-api/oa-examples/common.oa-example.mjs +3 -0
- package/dist/open-api/routes/routes.js +676 -132
- package/dist/pbds/pbd.controller.mjs +17 -3
- package/dist/pcis/pci.controller.mjs +16 -3
- package/dist/pgpus/pgpu.controller.mjs +16 -3
- package/dist/pifs/pif.controller.mjs +44 -8
- package/dist/pools/pool.controller.mjs +154 -9
- package/dist/proxies/proxy.controller.mjs +22 -4
- package/dist/restore-logs/restore-log.controller.mjs +36 -19
- package/dist/schedules/schedule.controller.mjs +33 -3
- package/dist/servers/server.controller.mjs +65 -5
- package/dist/sms/sm.controller.mjs +14 -2
- package/dist/srs/sr.controller.mjs +62 -10
- package/dist/tasks/task.controller.mjs +75 -11
- package/dist/users/user.controller.mjs +115 -16
- package/dist/vbds/vbd.controller.mjs +65 -31
- package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +36 -6
- package/dist/vdis/vdi.controller.mjs +69 -8
- package/dist/vifs/vif.controller.mjs +43 -7
- package/dist/vm-controller/vm-controller.controller.mjs +62 -9
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +70 -8
- package/dist/vm-templates/vm-template.controller.mjs +71 -8
- package/dist/vms/vm.controller.mjs +164 -12
- package/open-api/spec/swagger.json +10907 -3265
- package/package.json +4 -3
|
@@ -12,9 +12,10 @@ import { inject } from 'inversify';
|
|
|
12
12
|
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
13
13
|
import { Deprecated, Example, Get, Hidden, Middlewares, Path, Query, Request, Response, Route, Security, Tags, } from 'tsoa';
|
|
14
14
|
import { provide } from 'inversify-binding-decorators';
|
|
15
|
+
import { acl, autoBindService } from '../middlewares/acl.middleware.mjs';
|
|
15
16
|
import { backupLog, backupLogIds, partialBackupLogs } from '../open-api/oa-examples/backup-log.oa-example.mjs';
|
|
16
17
|
import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
|
|
17
|
-
import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
18
|
+
import { badRequestResp, forbiddenOperationResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
18
19
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
19
20
|
import { limitAndFilterArray, safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
20
21
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
@@ -33,24 +34,30 @@ let BackupJobController = class BackupJobController extends XoController {
|
|
|
33
34
|
const backupJobs = allJobs.filter(job => this.#backupJobService.isBackupJob(job));
|
|
34
35
|
return backupJobs;
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!this.#backupJobService.isBackupJob(job)) {
|
|
39
|
-
throw noSuchObject(id, 'backup-job');
|
|
40
|
-
}
|
|
41
|
-
return job;
|
|
37
|
+
getCollectionObject(id) {
|
|
38
|
+
return this.#backupJobService.getBackupJob(id);
|
|
42
39
|
}
|
|
43
40
|
/**
|
|
41
|
+
* Returns all backup jobs that match the following privilege:
|
|
42
|
+
* - resource: backup-job, action: read
|
|
44
43
|
*
|
|
45
44
|
* @example fields "name,mode,type,id"
|
|
46
45
|
* @example filter "type:backup"
|
|
47
46
|
* @example limit 42
|
|
48
47
|
*/
|
|
49
48
|
async getBackupJobs(req, fields, ndjson, markdown, filter, limit) {
|
|
50
|
-
const backupJobs = await this.getObjects({ filter
|
|
51
|
-
return this.sendObjects(Object.values(backupJobs), req,
|
|
49
|
+
const backupJobs = await this.getObjects({ filter });
|
|
50
|
+
return this.sendObjects(Object.values(backupJobs), req, {
|
|
51
|
+
path: 'backup-jobs',
|
|
52
|
+
limit,
|
|
53
|
+
privilege: { action: 'read', resource: 'backup-job' },
|
|
54
|
+
});
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
57
|
+
*
|
|
58
|
+
* Required privilege:
|
|
59
|
+
* - resource: backup-job, action: read
|
|
60
|
+
*
|
|
54
61
|
* @example id "d33f3dc1-92b4-469c-ad58-4c2a106a4721"
|
|
55
62
|
*/
|
|
56
63
|
getBackupJob(id) {
|
|
@@ -61,6 +68,7 @@ __decorate([
|
|
|
61
68
|
Example(vmBackupJobIds),
|
|
62
69
|
Example(partialVmBackupJobs),
|
|
63
70
|
Get(''),
|
|
71
|
+
Security('*', ['acl']),
|
|
64
72
|
__param(0, Request()),
|
|
65
73
|
__param(1, Query()),
|
|
66
74
|
__param(2, Query()),
|
|
@@ -70,8 +78,15 @@ __decorate([
|
|
|
70
78
|
], BackupJobController.prototype, "getBackupJobs", null);
|
|
71
79
|
__decorate([
|
|
72
80
|
Example(vmBackupJob),
|
|
73
|
-
Response(notFoundResp.status, notFoundResp.description),
|
|
74
81
|
Get('{id}'),
|
|
82
|
+
Middlewares(acl({
|
|
83
|
+
resource: 'backup-job',
|
|
84
|
+
action: 'read',
|
|
85
|
+
objectId: 'params.id',
|
|
86
|
+
getObject: autoBindService(BackupJobService, 'getBackupJob'),
|
|
87
|
+
})),
|
|
88
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
89
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
75
90
|
__param(0, Path())
|
|
76
91
|
], BackupJobController.prototype, "getBackupJob", null);
|
|
77
92
|
BackupJobController = __decorate([
|
|
@@ -113,6 +128,8 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
113
128
|
return backupJob;
|
|
114
129
|
}
|
|
115
130
|
/**
|
|
131
|
+
* Returns all VM backup jobs that match the following privilege:
|
|
132
|
+
* - resource: backup-job, action: read
|
|
116
133
|
*
|
|
117
134
|
* @example fields "name,mode,id"
|
|
118
135
|
* @example filter "mode:delta"
|
|
@@ -120,7 +137,11 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
120
137
|
*/
|
|
121
138
|
async getVmBackupJobs(req, fields, ndjson, markdown, filter, limit) {
|
|
122
139
|
const vmBackupJobs = await this.restApi.xoApp.getAllJobs('backup');
|
|
123
|
-
return this.sendObjects(limitAndFilterArray(vmBackupJobs, { filter
|
|
140
|
+
return this.sendObjects(limitAndFilterArray(vmBackupJobs, { filter }), req, {
|
|
141
|
+
path: 'backup-jobs',
|
|
142
|
+
limit,
|
|
143
|
+
privilege: { action: 'read', resource: 'backup-job' },
|
|
144
|
+
});
|
|
124
145
|
}
|
|
125
146
|
// For compatibility, redirect /backup/jobs/:id to /backup/jobs/vm/:id
|
|
126
147
|
async redirectToVmBackupJob(req, id) {
|
|
@@ -134,6 +155,8 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
134
155
|
return this.getObject(id, 'backup');
|
|
135
156
|
}
|
|
136
157
|
/**
|
|
158
|
+
* Returns all metadata backup jobs that match the following privilege:
|
|
159
|
+
* - resource: backup-job, action: read
|
|
137
160
|
*
|
|
138
161
|
* @example fields "name,xoMetadata,id"
|
|
139
162
|
* @example filter "xoMetadata?"
|
|
@@ -141,7 +164,11 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
141
164
|
*/
|
|
142
165
|
async getMetadataBackupJobs(req, fields, ndjson, markdown, filter, limit) {
|
|
143
166
|
const metadataBackupJobs = await this.restApi.xoApp.getAllJobs('metadataBackup');
|
|
144
|
-
return this.sendObjects(limitAndFilterArray(metadataBackupJobs, { filter
|
|
167
|
+
return this.sendObjects(limitAndFilterArray(metadataBackupJobs, { filter }), req, {
|
|
168
|
+
path: 'backup-jobs',
|
|
169
|
+
limit,
|
|
170
|
+
privilege: { action: 'read', resource: 'backup-job' },
|
|
171
|
+
});
|
|
145
172
|
}
|
|
146
173
|
/**
|
|
147
174
|
* @example id "b50f95fd-f6b7-4027-87b6-6a02c7dcd5f5"
|
|
@@ -150,6 +177,8 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
150
177
|
return this.getObject(id, 'metadataBackup');
|
|
151
178
|
}
|
|
152
179
|
/**
|
|
180
|
+
* Returns all mirror backup jobs that match the following privilege:
|
|
181
|
+
* - resource: backup-job, action: read
|
|
153
182
|
*
|
|
154
183
|
* @example fields "name,mode,id"
|
|
155
184
|
* @example filter "mode:delta"
|
|
@@ -157,7 +186,11 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
157
186
|
*/
|
|
158
187
|
async getMirrorBackupJobs(req, fields, ndjson, markdown, filter, limit) {
|
|
159
188
|
const mirrorBackupJobs = await this.restApi.xoApp.getAllJobs('mirrorBackup');
|
|
160
|
-
return this.sendObjects(limitAndFilterArray(mirrorBackupJobs, { filter
|
|
189
|
+
return this.sendObjects(limitAndFilterArray(mirrorBackupJobs, { filter }), req, {
|
|
190
|
+
path: 'backup-jobs',
|
|
191
|
+
limit,
|
|
192
|
+
privilege: { action: 'read', resource: 'backup-job' },
|
|
193
|
+
});
|
|
161
194
|
}
|
|
162
195
|
/**
|
|
163
196
|
* @example id "e680c14c-ab52-45c8-bb0e-bd4ca12ea8f9"
|
|
@@ -166,6 +199,9 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
166
199
|
return this.getObject(id, 'mirrorBackup');
|
|
167
200
|
}
|
|
168
201
|
/**
|
|
202
|
+
* Returns all backup logs that match the following privilege:
|
|
203
|
+
* - resource: backup-log, action: read
|
|
204
|
+
*
|
|
169
205
|
* @example fields "jobName,status,data"
|
|
170
206
|
* @example filter "status:success"
|
|
171
207
|
* @example limit 42
|
|
@@ -178,8 +214,12 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
|
|
|
178
214
|
}
|
|
179
215
|
return userFilter(log);
|
|
180
216
|
};
|
|
181
|
-
const logs = (await this.restApi.xoApp.getBackupNgLogsSorted({ filter: predicate
|
|
182
|
-
return this.sendObjects(logs, req,
|
|
217
|
+
const logs = (await this.restApi.xoApp.getBackupNgLogsSorted({ filter: predicate }));
|
|
218
|
+
return this.sendObjects(logs, req, {
|
|
219
|
+
path: 'backup-logs',
|
|
220
|
+
limit,
|
|
221
|
+
privilege: { action: 'read', resource: 'backup-log' },
|
|
222
|
+
});
|
|
183
223
|
}
|
|
184
224
|
/**
|
|
185
225
|
* @example id "1753776067468"
|
|
@@ -197,6 +237,7 @@ __decorate([
|
|
|
197
237
|
Example(partialVmBackupJobs),
|
|
198
238
|
Deprecated(),
|
|
199
239
|
Get('jobs/vm'),
|
|
240
|
+
Security('*', ['acl']),
|
|
200
241
|
Tags('backup-jobs'),
|
|
201
242
|
__param(0, Request()),
|
|
202
243
|
__param(1, Query()),
|
|
@@ -225,6 +266,7 @@ __decorate([
|
|
|
225
266
|
Example(partialMetadataBackupJobs),
|
|
226
267
|
Deprecated(),
|
|
227
268
|
Get('jobs/metadata'),
|
|
269
|
+
Security('*', ['acl']),
|
|
228
270
|
Tags('backup-jobs'),
|
|
229
271
|
__param(0, Request()),
|
|
230
272
|
__param(1, Query()),
|
|
@@ -246,6 +288,7 @@ __decorate([
|
|
|
246
288
|
Example(partialMirrorBackupJobs),
|
|
247
289
|
Deprecated(),
|
|
248
290
|
Get('jobs/mirror'),
|
|
291
|
+
Security('*', ['acl']),
|
|
249
292
|
Tags('backup-jobs'),
|
|
250
293
|
__param(0, Request()),
|
|
251
294
|
__param(1, Query()),
|
|
@@ -267,6 +310,7 @@ __decorate([
|
|
|
267
310
|
Example(partialBackupLogs),
|
|
268
311
|
Deprecated(),
|
|
269
312
|
Get('logs'),
|
|
313
|
+
Security('*', ['acl']),
|
|
270
314
|
Tags('backup-logs'),
|
|
271
315
|
__param(0, Request()),
|
|
272
316
|
__param(1, Query()),
|
|
@@ -8,6 +8,13 @@ export class BackupJobService {
|
|
|
8
8
|
constructor(restApi) {
|
|
9
9
|
this.#restApi = restApi;
|
|
10
10
|
}
|
|
11
|
+
async getBackupJob(id) {
|
|
12
|
+
const job = await this.#restApi.xoApp.getJob(id);
|
|
13
|
+
if (!this.isBackupJob(job)) {
|
|
14
|
+
throw noSuchObject(id, 'backup-job');
|
|
15
|
+
}
|
|
16
|
+
return job;
|
|
17
|
+
}
|
|
11
18
|
isBackupJob(anyJob) {
|
|
12
19
|
return this.#backupJobTypes.includes(anyJob.type);
|
|
13
20
|
}
|
|
@@ -7,15 +7,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
10
|
+
import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
|
-
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
13
12
|
import { provide } from 'inversify-binding-decorators';
|
|
14
13
|
import { backupLog, backupLogIds, partialBackupLogs } from '../open-api/oa-examples/backup-log.oa-example.mjs';
|
|
15
14
|
import { BackupLogService } from './backup-log.service.mjs';
|
|
16
15
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
17
|
-
import { badRequestResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
16
|
+
import { badRequestResp, forbiddenOperationResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
18
17
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
18
|
+
import { acl, autoBindService } from '../middlewares/acl.middleware.mjs';
|
|
19
19
|
let BackupLogController = class BackupLogController extends XoController {
|
|
20
20
|
#backupLogService;
|
|
21
21
|
constructor(restApi, backupLogService) {
|
|
@@ -25,23 +25,28 @@ let BackupLogController = class BackupLogController extends XoController {
|
|
|
25
25
|
getAllCollectionObjects() {
|
|
26
26
|
return this.restApi.xoApp.getBackupNgLogsSorted({ filter: this.#backupLogService.isBackupLog });
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!this.#backupLogService.isBackupLog(log)) {
|
|
31
|
-
throw noSuchObject('backup-log');
|
|
32
|
-
}
|
|
33
|
-
return log;
|
|
28
|
+
getCollectionObject(id) {
|
|
29
|
+
return this.#backupLogService.getBackupLog(id);
|
|
34
30
|
}
|
|
35
31
|
/**
|
|
32
|
+
* Returns all backup logs that match the following privilege:
|
|
33
|
+
* - resource: backup-log, action: read
|
|
34
|
+
*
|
|
36
35
|
* @example fields "jobName,status,data"
|
|
37
36
|
* @example filter "status:success"
|
|
38
37
|
* @example limit 42
|
|
39
38
|
*/
|
|
40
39
|
async getBackupLogs(req, fields, ndjson, markdown, filter, limit) {
|
|
41
|
-
const backupLogs = await this.getObjects({ filter
|
|
42
|
-
return this.sendObjects(Object.values(backupLogs), req
|
|
40
|
+
const backupLogs = await this.getObjects({ filter });
|
|
41
|
+
return this.sendObjects(Object.values(backupLogs), req, {
|
|
42
|
+
limit,
|
|
43
|
+
privilege: { action: 'read', resource: 'backup-log' },
|
|
44
|
+
});
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
47
|
+
* Required privilege:
|
|
48
|
+
* - resource: backup-log, action: read
|
|
49
|
+
*
|
|
45
50
|
* @example id "1753776067468"
|
|
46
51
|
*/
|
|
47
52
|
getBackupLog(id) {
|
|
@@ -52,6 +57,7 @@ __decorate([
|
|
|
52
57
|
Example(backupLogIds),
|
|
53
58
|
Example(partialBackupLogs),
|
|
54
59
|
Get(''),
|
|
60
|
+
Security('*', ['acl']),
|
|
55
61
|
__param(0, Request()),
|
|
56
62
|
__param(1, Query()),
|
|
57
63
|
__param(2, Query()),
|
|
@@ -62,6 +68,14 @@ __decorate([
|
|
|
62
68
|
__decorate([
|
|
63
69
|
Example(backupLog),
|
|
64
70
|
Get('{id}'),
|
|
71
|
+
Middlewares(acl({
|
|
72
|
+
resource: 'backup-log',
|
|
73
|
+
action: 'read',
|
|
74
|
+
objectId: 'params.id',
|
|
75
|
+
getObject: autoBindService(BackupLogService, 'getBackupLog'),
|
|
76
|
+
})),
|
|
77
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
78
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
65
79
|
__param(0, Path())
|
|
66
80
|
], BackupLogController.prototype, "getBackupLog", null);
|
|
67
81
|
BackupLogController = __decorate([
|
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
1
2
|
export class BackupLogService {
|
|
3
|
+
#restApi;
|
|
4
|
+
constructor(restApi) {
|
|
5
|
+
this.#restApi = restApi;
|
|
6
|
+
}
|
|
2
7
|
isBackupLog(log) {
|
|
3
8
|
return log.message === 'backup';
|
|
4
9
|
}
|
|
10
|
+
async getBackupLog(id) {
|
|
11
|
+
const log = await this.#restApi.xoApp.getBackupNgLogs(id);
|
|
12
|
+
if (!this.isBackupLog(log)) {
|
|
13
|
+
throw noSuchObject('backup-log');
|
|
14
|
+
}
|
|
15
|
+
return log;
|
|
16
|
+
}
|
|
17
|
+
async getRestoreLog(id) {
|
|
18
|
+
const log = await this.#restApi.xoApp.getBackupNgLogs(id);
|
|
19
|
+
if (this.isBackupLog(log)) {
|
|
20
|
+
throw noSuchObject('restore-log');
|
|
21
|
+
}
|
|
22
|
+
return log;
|
|
23
|
+
}
|
|
5
24
|
getVmBackupTaskLog(log, vmId) {
|
|
6
25
|
return log.tasks?.find(task => task.data?.id === vmId);
|
|
7
26
|
}
|
|
@@ -7,10 +7,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
10
|
+
import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
12
|
import { provide } from 'inversify-binding-decorators';
|
|
13
|
-
import {
|
|
13
|
+
import { acl } from '../middlewares/acl.middleware.mjs';
|
|
14
|
+
import { badRequestResp, forbiddenOperationResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
14
15
|
import { backupRepositoryIds, partialBackupRepositories, backupRepository, } from '../open-api/oa-examples/backup-repository.oa-example.mjs';
|
|
15
16
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
16
17
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
@@ -26,14 +27,23 @@ let BackupRepositoryController = class BackupRepositoryController extends XoCont
|
|
|
26
27
|
return this.restApi.xoApp.getRemote(id);
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
30
|
+
* Returns all backup repositories that match the following privilege:
|
|
31
|
+
* - resource: backup-repository, action: read
|
|
32
|
+
*
|
|
29
33
|
* @example fields "id,name,enabled"
|
|
30
34
|
* @example filter "enabled?"
|
|
31
35
|
* @example limit 42
|
|
32
36
|
*/
|
|
33
37
|
async getRepositories(req, fields, ndjson, markdown, filter, limit) {
|
|
34
|
-
return this.sendObjects(Object.values(await this.getObjects({ filter
|
|
38
|
+
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
|
39
|
+
limit,
|
|
40
|
+
privilege: { action: 'read', resource: 'backup-repository' },
|
|
41
|
+
});
|
|
35
42
|
}
|
|
36
43
|
/**
|
|
44
|
+
* Required privilege:
|
|
45
|
+
* - resource: backup-repository, action: read
|
|
46
|
+
*
|
|
37
47
|
* @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
|
|
38
48
|
*/
|
|
39
49
|
getRepository(id) {
|
|
@@ -44,6 +54,7 @@ __decorate([
|
|
|
44
54
|
Example(backupRepositoryIds),
|
|
45
55
|
Example(partialBackupRepositories),
|
|
46
56
|
Get(''),
|
|
57
|
+
Security('*', ['acl']),
|
|
47
58
|
__param(0, Request()),
|
|
48
59
|
__param(1, Query()),
|
|
49
60
|
__param(2, Query()),
|
|
@@ -54,6 +65,13 @@ __decorate([
|
|
|
54
65
|
__decorate([
|
|
55
66
|
Example(backupRepository),
|
|
56
67
|
Get('{id}'),
|
|
68
|
+
Middlewares(acl({
|
|
69
|
+
resource: 'backup-repository',
|
|
70
|
+
action: 'read',
|
|
71
|
+
objectId: 'params.id',
|
|
72
|
+
getObject: ({ restApi }) => restApi.xoApp.getRemote,
|
|
73
|
+
})),
|
|
74
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
57
75
|
Response(notFoundResp.status, notFoundResp.description),
|
|
58
76
|
__param(0, Path())
|
|
59
77
|
], BackupRepositoryController.prototype, "getRepository", null);
|
|
@@ -11,6 +11,7 @@ export class Subscriber {
|
|
|
11
11
|
#connection;
|
|
12
12
|
#isAlive;
|
|
13
13
|
#cleanupCallbacks = new Set();
|
|
14
|
+
#userId;
|
|
14
15
|
get id() {
|
|
15
16
|
return this.#id;
|
|
16
17
|
}
|
|
@@ -20,13 +21,17 @@ export class Subscriber {
|
|
|
20
21
|
get connection() {
|
|
21
22
|
return this.#connection;
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
get userId() {
|
|
25
|
+
return this.#userId;
|
|
26
|
+
}
|
|
27
|
+
constructor(connection, manager, userId) {
|
|
24
28
|
this.#id = crypto.randomUUID();
|
|
25
29
|
connection.on('close', () => this.clear());
|
|
26
30
|
manager.addSubscriber(this);
|
|
27
31
|
this.#connection = connection;
|
|
28
32
|
this.#manager = manager;
|
|
29
33
|
this.#isAlive = true;
|
|
34
|
+
this.#userId = userId;
|
|
30
35
|
}
|
|
31
36
|
#safeWrite(payload) {
|
|
32
37
|
const ok = this.#connection.write(payload);
|
|
@@ -60,24 +65,23 @@ export class Subscriber {
|
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
export class XoListener extends Listener {
|
|
63
|
-
#type;
|
|
64
68
|
#alarmService;
|
|
65
69
|
constructor(type, eventEmitter, alarmService) {
|
|
66
|
-
super(eventEmitter, ['add', 'update', 'remove']);
|
|
67
|
-
this.#type = type;
|
|
70
|
+
super(eventEmitter, ['add', 'update', 'remove'], type);
|
|
68
71
|
this.#alarmService = alarmService;
|
|
69
72
|
}
|
|
70
|
-
handleData({ fields, event }, object, previousObj) {
|
|
73
|
+
async handleData({ fields, event, subscriber }, object, previousObj) {
|
|
71
74
|
let _object = object;
|
|
72
75
|
let _prevObject = previousObj;
|
|
73
|
-
|
|
76
|
+
///
|
|
77
|
+
if (this.type === 'alarm' || this.type === 'message') {
|
|
74
78
|
const isAlarm = (object) => object !== undefined && 'type' in object && object.type === 'message' && this.#alarmService.isAlarm(object);
|
|
75
79
|
const objectIsAlarm = isAlarm(object);
|
|
76
80
|
const prevObjectIsAlarm = isAlarm(previousObj);
|
|
77
81
|
// If we are in an alarm listener and the objects are messages
|
|
78
82
|
// we clean them to ensure they are not sent via the SSE
|
|
79
83
|
// Same if we are in a message listener and the objects are alarms
|
|
80
|
-
if (this
|
|
84
|
+
if (this.type === 'alarm') {
|
|
81
85
|
_object = objectIsAlarm ? this.#alarmService.parseAlarm(object) : undefined;
|
|
82
86
|
_prevObject = prevObjectIsAlarm ? this.#alarmService.parseAlarm(previousObj) : undefined;
|
|
83
87
|
}
|
|
@@ -89,6 +93,17 @@ export class XoListener extends Listener {
|
|
|
89
93
|
if (_object === undefined && _prevObject === undefined) {
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
96
|
+
const aclEvent = await this.getAclEvent({
|
|
97
|
+
event,
|
|
98
|
+
object: _object,
|
|
99
|
+
previousObject: _prevObject,
|
|
100
|
+
userId: subscriber.userId,
|
|
101
|
+
});
|
|
102
|
+
// If the user has no 'read' privileges for the changes, don't send the update
|
|
103
|
+
if (aclEvent === undefined) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
event = aclEvent;
|
|
92
107
|
if (fields !== '*') {
|
|
93
108
|
if (_object !== undefined) {
|
|
94
109
|
_object = pick(_object, fields);
|
|
@@ -102,7 +117,7 @@ export class XoListener extends Listener {
|
|
|
102
117
|
return;
|
|
103
118
|
}
|
|
104
119
|
// if _object === undefined, this means we are on a remove event, so _prevObject will not be undefined
|
|
105
|
-
return { $subscription: this
|
|
120
|
+
return { $subscription: this.type, event, ...(_object ?? _prevObject) };
|
|
106
121
|
}
|
|
107
122
|
}
|
|
108
123
|
export class PingListener extends Listener {
|
|
@@ -113,7 +128,7 @@ export class PingListener extends Listener {
|
|
|
113
128
|
this.eventEmitter.emit('ping');
|
|
114
129
|
}, 1000 * 30);
|
|
115
130
|
}
|
|
116
|
-
handleData() {
|
|
131
|
+
async handleData() {
|
|
117
132
|
return { ping: Date.now() };
|
|
118
133
|
}
|
|
119
134
|
clear() {
|
|
@@ -70,12 +70,14 @@ let EventController = class EventController extends Controller {
|
|
|
70
70
|
};
|
|
71
71
|
__decorate([
|
|
72
72
|
Get(''),
|
|
73
|
+
Security('*', ['acl']),
|
|
73
74
|
SuccessResponse(200, 'OK'),
|
|
74
75
|
__param(0, Request())
|
|
75
76
|
], EventController.prototype, "openSseConnection", null);
|
|
76
77
|
__decorate([
|
|
77
78
|
Example(addSubscription),
|
|
78
79
|
Post('{id}/subscriptions'),
|
|
80
|
+
Security('*', ['acl']),
|
|
79
81
|
Middlewares(json()),
|
|
80
82
|
SuccessResponse(createdResp.status, createdResp.description),
|
|
81
83
|
Response(notFoundResp.status, notFoundResp.description),
|
|
@@ -84,6 +86,7 @@ __decorate([
|
|
|
84
86
|
], EventController.prototype, "addSubscription", null);
|
|
85
87
|
__decorate([
|
|
86
88
|
Delete('{id}/subscriptions/{subscriptionId}'),
|
|
89
|
+
Security('*', ['acl']),
|
|
87
90
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
88
91
|
Response(notFoundResp.status, notFoundResp.description),
|
|
89
92
|
__param(0, Path()),
|
|
@@ -58,7 +58,8 @@ export class EventService {
|
|
|
58
58
|
log.error(error);
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
|
-
const
|
|
61
|
+
const user = this.#restApi.getCurrentUser();
|
|
62
|
+
const subscriber = new Subscriber(connection, this.#subscriberManager, user.id);
|
|
62
63
|
subscriber.broadcast('init', { id: subscriber.id });
|
|
63
64
|
this.addListenerFor(subscriber.id, { type: 'ping' });
|
|
64
65
|
log.debug(`new SSE subscriber added: ${subscriber.id}`);
|