@xen-orchestra/rest-api 0.29.0 → 0.30.0

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.
Files changed (53) hide show
  1. package/README.md +108 -1
  2. package/dist/abstract-classes/base-controller.mjs +18 -3
  3. package/dist/abstract-classes/listener.mjs +116 -12
  4. package/dist/acl-privileges/acl-privilege.controller.mjs +172 -0
  5. package/dist/acl-roles/acl-role.controller.mjs +384 -0
  6. package/dist/alarms/alarm.controller.mjs +22 -9
  7. package/dist/alarms/alarm.service.mjs +8 -0
  8. package/dist/backup-archives/backup-archive.controller.mjs +30 -21
  9. package/dist/backup-archives/backup-archive.service.mjs +21 -0
  10. package/dist/backup-jobs/backup-job.controller.mjs +59 -15
  11. package/dist/backup-jobs/backup-job.service.mjs +7 -0
  12. package/dist/backup-logs/backup-log.controller.mjs +25 -11
  13. package/dist/backup-logs/backup-log.service.mjs +19 -0
  14. package/dist/backup-repositories/backup-repositories.controller.mjs +21 -3
  15. package/dist/events/event.class.mjs +24 -9
  16. package/dist/events/event.controller.mjs +3 -0
  17. package/dist/events/event.service.mjs +2 -1
  18. package/dist/groups/group.controller.mjs +90 -6
  19. package/dist/hosts/host.controller.mjs +78 -7
  20. package/dist/ioc/ioc.mjs +13 -4
  21. package/dist/messages/message.controller.mjs +29 -8
  22. package/dist/middlewares/acl.middleware.mjs +202 -0
  23. package/dist/middlewares/authentication.middleware.mjs +15 -6
  24. package/dist/middlewares/tsoa-to-xo-error.middleware.mjs +19 -1
  25. package/dist/networks/network.controller.mjs +60 -9
  26. package/dist/open-api/oa-examples/acl-privilege.oa-example.mjs +25 -0
  27. package/dist/open-api/oa-examples/acl-role.oa-example.mjs +22 -0
  28. package/dist/open-api/oa-examples/backup-archive.oa-example.mjs +6 -6
  29. package/dist/open-api/oa-examples/common.oa-example.mjs +3 -0
  30. package/dist/open-api/routes/routes.js +676 -132
  31. package/dist/pbds/pbd.controller.mjs +17 -3
  32. package/dist/pcis/pci.controller.mjs +16 -3
  33. package/dist/pgpus/pgpu.controller.mjs +16 -3
  34. package/dist/pifs/pif.controller.mjs +44 -8
  35. package/dist/pools/pool.controller.mjs +154 -9
  36. package/dist/proxies/proxy.controller.mjs +22 -4
  37. package/dist/restore-logs/restore-log.controller.mjs +36 -19
  38. package/dist/schedules/schedule.controller.mjs +33 -3
  39. package/dist/servers/server.controller.mjs +65 -5
  40. package/dist/sms/sm.controller.mjs +14 -2
  41. package/dist/srs/sr.controller.mjs +62 -10
  42. package/dist/tasks/task.controller.mjs +71 -11
  43. package/dist/users/user.controller.mjs +115 -16
  44. package/dist/vbds/vbd.controller.mjs +65 -31
  45. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +36 -6
  46. package/dist/vdis/vdi.controller.mjs +69 -8
  47. package/dist/vifs/vif.controller.mjs +43 -7
  48. package/dist/vm-controller/vm-controller.controller.mjs +62 -9
  49. package/dist/vm-snapshots/vm-snapshot.controller.mjs +70 -8
  50. package/dist/vm-templates/vm-template.controller.mjs +71 -8
  51. package/dist/vms/vm.controller.mjs +164 -12
  52. package/open-api/spec/swagger.json +10907 -3265
  53. package/package.json +4 -3
@@ -20,6 +20,7 @@ import { RestApi } from '../rest-api/rest-api.mjs';
20
20
  import { limitAndFilterArray } from '../helpers/utils.helper.mjs';
21
21
  import { partialUsers, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
22
22
  import { partialTasks, taskIds } from '../open-api/oa-examples/task.oa-example.mjs';
23
+ import { acl, actionFromBody } from '../middlewares/acl.middleware.mjs';
23
24
  let GroupController = class GroupController extends XoController {
24
25
  #userService;
25
26
  constructor(restApi, userService) {
@@ -34,20 +35,35 @@ let GroupController = class GroupController extends XoController {
34
35
  return this.restApi.xoApp.getGroup(id);
35
36
  }
36
37
  /**
38
+ * Returns all groups that match the following privilege:
39
+ * - resource: group, action: read
40
+ *
37
41
  * @example fields "name,id,users"
38
42
  * @example filter "users:length:>0"
39
43
  * @example limit 42
40
44
  */
41
45
  async getGroups(req, fields, ndjson, markdown, filter, limit) {
42
- return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req);
46
+ return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
47
+ limit,
48
+ privilege: { action: 'read', resource: 'group' },
49
+ });
43
50
  }
44
51
  /**
52
+ * Required privilege:
53
+ * - resource: group, action: read
54
+ *
45
55
  * @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
46
56
  */
47
57
  getGroup(id) {
48
58
  return this.getObject(id);
49
59
  }
50
60
  /**
61
+ * You cannot patch `synchronized` groups (even with the right privilege)
62
+ *
63
+ * Required privileges:
64
+ * - resource: group, action: update (grants all fields)
65
+ * - resource: group, action: update:name (if name is passed)
66
+ *
51
67
  * @example id "c98395a7-26d8-4e09-b055-d5f0f4a98312"
52
68
  * @example body { "name": "new group name" }
53
69
  */
@@ -59,6 +75,9 @@ let GroupController = class GroupController extends XoController {
59
75
  await this.restApi.xoApp.updateGroup(group.id, body);
60
76
  }
61
77
  /**
78
+ * Required privilege:
79
+ * - resource: group, action: create
80
+ *
62
81
  * @example body {
63
82
  * "name": "new group"
64
83
  * }
@@ -68,6 +87,9 @@ let GroupController = class GroupController extends XoController {
68
87
  return { id: group.id };
69
88
  }
70
89
  /**
90
+ * Required privilege:
91
+ * - resource: group, action: delete
92
+ *
71
93
  * @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
72
94
  */
73
95
  async deleteGroup(id) {
@@ -75,6 +97,11 @@ let GroupController = class GroupController extends XoController {
75
97
  await this.restApi.xoApp.deleteGroup(groupId);
76
98
  }
77
99
  /**
100
+ * You cannot manage users of `synchronized` groups (even with the right privilege)
101
+ *
102
+ * Required privilege:
103
+ * - resource: group, action: update:users
104
+ *
78
105
  * @example id "c98395a7-26d8-4e09-b055-d5f0f4a98312"
79
106
  * @example userId "722d17b9-699b-49d2-8193-be1ac573d3de"
80
107
  */
@@ -86,6 +113,11 @@ let GroupController = class GroupController extends XoController {
86
113
  await this.restApi.xoApp.removeUserFromGroup(userId, group.id);
87
114
  }
88
115
  /**
116
+ * You cannot manage users of `synchronized` groups (even with the right privilege)
117
+ *
118
+ * Required privilege:
119
+ * - resource: group, action: update:users
120
+ *
89
121
  * @example id "6c81b5e1-afc1-43ea-8f8d-939ceb5f3f90"
90
122
  * @example userId "722d17b9-699b-49d2-8193-be1ac573d3de"
91
123
  */
@@ -97,6 +129,9 @@ let GroupController = class GroupController extends XoController {
97
129
  await this.restApi.xoApp.addUserToGroup(userId, group.id);
98
130
  }
99
131
  /**
132
+ * Returns all users that match the following privilege:
133
+ * - resource: user, action: read
134
+ *
100
135
  * @example id "6c81b5e1-afc1-43ea-8f8d-939ceb5f3f90"
101
136
  * @example fields "permission,name,id"
102
137
  * @example filter "permission:none"
@@ -105,23 +140,35 @@ let GroupController = class GroupController extends XoController {
105
140
  async getGroupUsers(req, id, fields, ndjson, markdown, filter, limit) {
106
141
  const group = await this.getObject(id);
107
142
  const users = await Promise.all(group.users.map(id => this.#userService.getUser(id)));
108
- return this.sendObjects(limitAndFilterArray(users, { filter, limit }), req, 'users');
143
+ return this.sendObjects(limitAndFilterArray(users, { filter }), req, {
144
+ path: 'users',
145
+ limit,
146
+ privilege: { action: 'read', resource: 'user' },
147
+ });
109
148
  }
110
149
  /**
150
+ * Returns all tasks that match the following privilege:
151
+ * - resource: task, action: read
152
+ *
111
153
  * @example id "6c81b5e1-afc1-43ea-8f8d-939ceb5f3f90"
112
154
  * @example fields "id,status,properties"
113
155
  * @example filter "status:failure"
114
156
  * @example limit 42
115
157
  */
116
158
  async getGroupTasks(req, id, fields, ndjson, markdown, filter, limit) {
117
- const tasks = await this.getTasksForObject(id, { filter, limit });
118
- return this.sendObjects(Object.values(tasks), req, 'tasks');
159
+ const tasks = await this.getTasksForObject(id, { filter });
160
+ return this.sendObjects(Object.values(tasks), req, {
161
+ path: 'tasks',
162
+ limit,
163
+ privilege: { action: 'read', resource: 'task' },
164
+ });
119
165
  }
120
166
  };
121
167
  __decorate([
122
168
  Example(groupIds),
123
169
  Example(partialGroups),
124
170
  Get(''),
171
+ Security('*', ['acl']),
125
172
  __param(0, Request()),
126
173
  __param(1, Query()),
127
174
  __param(2, Query()),
@@ -132,12 +179,27 @@ __decorate([
132
179
  __decorate([
133
180
  Example(group),
134
181
  Get('{id}'),
182
+ Middlewares(acl({
183
+ resource: 'group',
184
+ action: 'read',
185
+ objectId: 'params.id',
186
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
187
+ })),
188
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
135
189
  Response(notFoundResp.status, notFoundResp.description),
136
190
  __param(0, Path())
137
191
  ], GroupController.prototype, "getGroup", null);
138
192
  __decorate([
139
193
  Patch('{id}'),
140
- Middlewares(json()),
194
+ Middlewares([
195
+ json(),
196
+ acl({
197
+ resource: 'group',
198
+ action: actionFromBody('update:name'),
199
+ objectId: 'params.id',
200
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
201
+ }),
202
+ ]),
141
203
  SuccessResponse(noContentResp.status, noContentResp.description),
142
204
  Response(notFoundResp.status, notFoundResp.description),
143
205
  Response(resourceAlreadyExists.status, resourceAlreadyExists.description),
@@ -148,20 +210,34 @@ __decorate([
148
210
  __decorate([
149
211
  Example(groupId),
150
212
  Post(''),
151
- Middlewares(json()),
213
+ Middlewares([json(), acl({ resource: 'group', action: 'create', object: ({ req }) => req.body })]),
152
214
  SuccessResponse(createdResp.status, createdResp.description),
215
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
153
216
  Response(invalidParameters.status, invalidParameters.description),
154
217
  Response(resourceAlreadyExists.status, resourceAlreadyExists.description),
155
218
  __param(0, Body())
156
219
  ], GroupController.prototype, "createGroup", null);
157
220
  __decorate([
158
221
  Delete('{id}'),
222
+ Middlewares(acl({
223
+ resource: 'group',
224
+ action: 'delete',
225
+ objectId: 'params.id',
226
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
227
+ })),
159
228
  SuccessResponse(noContentResp.status, noContentResp.description),
229
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
160
230
  Response(notFoundResp.status, notFoundResp.description),
161
231
  __param(0, Path())
162
232
  ], GroupController.prototype, "deleteGroup", null);
163
233
  __decorate([
164
234
  Delete('{id}/users/{userId}'),
235
+ Middlewares(acl({
236
+ resource: 'group',
237
+ action: 'update:users',
238
+ objectId: 'params.id',
239
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
240
+ })),
165
241
  SuccessResponse(noContentResp.status, noContentResp.description),
166
242
  Response(notFoundResp.status, notFoundResp.description),
167
243
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -170,6 +246,12 @@ __decorate([
170
246
  ], GroupController.prototype, "removeUserFromGroup", null);
171
247
  __decorate([
172
248
  Put('{id}/users/{userId}'),
249
+ Middlewares(acl({
250
+ resource: 'group',
251
+ action: 'update:users',
252
+ objectId: 'params.id',
253
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
254
+ })),
173
255
  SuccessResponse(noContentResp.status, noContentResp.description),
174
256
  Response(notFoundResp.status, notFoundResp.description),
175
257
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -180,6 +262,7 @@ __decorate([
180
262
  Example(userIds),
181
263
  Example(partialUsers),
182
264
  Get('{id}/users'),
265
+ Security('*', ['acl']),
183
266
  Tags('users'),
184
267
  Response(notFoundResp.status, notFoundResp.description),
185
268
  __param(0, Request()),
@@ -194,6 +277,7 @@ __decorate([
194
277
  Example(taskIds),
195
278
  Example(partialTasks),
196
279
  Get('{id}/tasks'),
280
+ Security('*', ['acl']),
197
281
  Tags('tasks'),
198
282
  Response(notFoundResp.status, notFoundResp.description),
199
283
  __param(0, Request()),
@@ -15,13 +15,14 @@ import { inject } from 'inversify';
15
15
  import { invalidParameters } from 'xo-common/api-errors.js';
16
16
  import { pipeline } from 'node:stream/promises';
17
17
  import { provide } from 'inversify-binding-decorators';
18
+ import { acl } from '../middlewares/acl.middleware.mjs';
18
19
  import { AlarmService } from '../alarms/alarm.service.mjs';
19
20
  import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
20
21
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
21
22
  import { host, hostIds, hostSmt, hostMissingPatches, hostStats, partialHosts, } from '../open-api/oa-examples/host.oa-example.mjs';
22
23
  import { RestApi } from '../rest-api/rest-api.mjs';
23
24
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
24
- import { asynchronousActionResp, badRequestResp, featureUnauthorized, internalServerErrorResp, invalidParameters as invalidParametersResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
25
+ import { asynchronousActionResp, badRequestResp, featureUnauthorized, forbiddenOperationResp, internalServerErrorResp, invalidParameters as invalidParametersResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
25
26
  import { HostService } from './host.service.mjs';
26
27
  import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
27
28
  import { partialTasks, taskIds, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
@@ -34,20 +35,32 @@ let HostController = class HostController extends XapiXoController {
34
35
  this.#hostService = hostService;
35
36
  }
36
37
  /**
38
+ * Returns all hosts that match the following privilege:
39
+ * - resource: host, action: read
40
+ *
37
41
  * @example fields "id,name_label,productBrand"
38
42
  * @example filter "productBrand:XCP-ng"
39
43
  * @example limit 42
40
44
  */
41
45
  getHosts(req, fields, ndjson, markdown, filter, limit) {
42
- return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
46
+ return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
47
+ limit,
48
+ privilege: { action: 'read', resource: 'host' },
49
+ });
43
50
  }
44
51
  /**
52
+ * Required privilege:
53
+ * - resource: host, action: read
54
+ *
45
55
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
46
56
  */
47
57
  getHost(id) {
48
58
  return this.getObject(id);
49
59
  }
50
60
  /**
61
+ * Required privilege:
62
+ * - resource: host, action: read
63
+ *
51
64
  * Host must be running
52
65
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
53
66
  */
@@ -55,6 +68,9 @@ let HostController = class HostController extends XapiXoController {
55
68
  return this.restApi.xoApp.getXapiHostStats(id, granularity);
56
69
  }
57
70
  /**
71
+ * Required privilege:
72
+ * - resource: host, action: export:logs
73
+ *
58
74
  * Host must be running
59
75
  *
60
76
  * Download the audit log of a host.
@@ -75,6 +91,9 @@ let HostController = class HostController extends XapiXoController {
75
91
  await pipeline(response.body, this.maybeCompressResponse(req, res));
76
92
  }
77
93
  /**
94
+ * Required privilege:
95
+ * - resource: host, action: export:logs
96
+ *
78
97
  * Host must be running
79
98
  *
80
99
  * Download all logs of a host.
@@ -90,6 +109,9 @@ let HostController = class HostController extends XapiXoController {
90
109
  await pipeline(response.body, res);
91
110
  }
92
111
  /**
112
+ * Returns all alarms that match the following privilege:
113
+ * - resource: alarm, action: read
114
+ *
93
115
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
94
116
  * @example fields "id,time"
95
117
  * @example filter "time:>1747053793"
@@ -99,11 +121,17 @@ let HostController = class HostController extends XapiXoController {
99
121
  const host = this.getObject(id);
100
122
  const alarms = this.#alarmService.getAlarms({
101
123
  filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${host.uuid}`,
124
+ });
125
+ return this.sendObjects(Object.values(alarms), req, {
126
+ path: 'alarms',
102
127
  limit,
128
+ privilege: { action: 'read', resource: 'alarm' },
103
129
  });
104
- return this.sendObjects(Object.values(alarms), req, 'alarms');
105
130
  }
106
131
  /**
132
+ * Required privilege:
133
+ * - resource: host, action: read
134
+ *
107
135
  * Returns a boolean indicating whether SMT (Simultaneous Multi-Threading) is enabled
108
136
  *
109
137
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
@@ -115,6 +143,9 @@ let HostController = class HostController extends XapiXoController {
115
143
  return { enabled };
116
144
  }
117
145
  /**
146
+ * Required privilege:
147
+ * - resource: host, action: read
148
+ *
118
149
  * Host must be running
119
150
  *
120
151
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
@@ -124,26 +155,43 @@ let HostController = class HostController extends XapiXoController {
124
155
  return missingPatches;
125
156
  }
126
157
  /**
158
+ * Returns all messages that match the following privilege:
159
+ * - resource: message, action: read
160
+ *
127
161
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
128
162
  * @example fields "name,id,$object"
129
163
  * @example filter "name:PBD_PLUG_FAILED_ON_SERVER_START"
130
164
  * @example limit 42
131
165
  */
132
166
  getHostMessages(req, id, fields, ndjson, markdown, filter, limit) {
133
- const messages = this.getMessagesForObject(id, { filter, limit });
134
- return this.sendObjects(Object.values(messages), req, 'messages');
167
+ const messages = this.getMessagesForObject(id, { filter });
168
+ return this.sendObjects(Object.values(messages), req, {
169
+ path: 'messages',
170
+ limit,
171
+ privilege: { action: 'read', resource: 'message' },
172
+ });
135
173
  }
136
174
  /**
175
+ * Returns all tasks that match the following privilege:
176
+ * - resource: task, action: read
177
+ *
137
178
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
138
179
  * @example fields "id,status,properties"
139
180
  * @example filter "status:failure"
140
181
  * @example limit 42
141
182
  */
142
183
  async getHostTasks(req, id, fields, ndjson, markdown, filter, limit) {
143
- const tasks = await this.getTasksForObject(id, { filter, limit });
144
- return this.sendObjects(Object.values(tasks), req, 'tasks');
184
+ const tasks = await this.getTasksForObject(id, { filter });
185
+ return this.sendObjects(Object.values(tasks), req, {
186
+ path: 'tasks',
187
+ limit,
188
+ privilege: { action: 'read', resource: 'task' },
189
+ });
145
190
  }
146
191
  /**
192
+ * Required privilege:
193
+ * - resource: host, action: update:tags
194
+ *
147
195
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
148
196
  * @example tag "from-rest-api"
149
197
  */
@@ -152,6 +200,9 @@ let HostController = class HostController extends XapiXoController {
152
200
  await host.$call('add_tags', tag);
153
201
  }
154
202
  /**
203
+ * Required privilege:
204
+ * - resource: host, action: update:tags
205
+ *
155
206
  * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
156
207
  * @example tag "from-rest-api"
157
208
  */
@@ -257,6 +308,7 @@ __decorate([
257
308
  Example(hostIds),
258
309
  Example(partialHosts),
259
310
  Get(''),
311
+ Security('*', ['acl']),
260
312
  __param(0, Request()),
261
313
  __param(1, Query()),
262
314
  __param(2, Query()),
@@ -267,12 +319,16 @@ __decorate([
267
319
  __decorate([
268
320
  Example(host),
269
321
  Get('{id}'),
322
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
323
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
270
324
  Response(notFoundResp.status, notFoundResp.description),
271
325
  __param(0, Path())
272
326
  ], HostController.prototype, "getHost", null);
273
327
  __decorate([
274
328
  Example(hostStats),
275
329
  Get('{id}/stats'),
330
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
331
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
276
332
  Response(notFoundResp.status, notFoundResp.description),
277
333
  Response(422, 'Invalid granularity'),
278
334
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
@@ -281,7 +337,9 @@ __decorate([
281
337
  ], HostController.prototype, "getHostStats", null);
282
338
  __decorate([
283
339
  Get('{id}/audit.txt'),
340
+ Middlewares(acl({ resource: 'host', action: 'export:logs', objectId: 'params.id' })),
284
341
  SuccessResponse(200, 'Download started', 'application/octet-stream'),
342
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
285
343
  Response(notFoundResp.status, notFoundResp.description),
286
344
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
287
345
  __param(0, Request()),
@@ -289,7 +347,9 @@ __decorate([
289
347
  ], HostController.prototype, "getAuditLog", null);
290
348
  __decorate([
291
349
  Get('{id}/logs.tgz'),
350
+ Middlewares(acl({ resource: 'host', action: 'export:logs', objectId: 'params.id' })),
292
351
  SuccessResponse(200, 'Download started', 'application/gzip'),
352
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
293
353
  Response(notFoundResp.status, notFoundResp.description),
294
354
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
295
355
  __param(0, Request()),
@@ -298,6 +358,7 @@ __decorate([
298
358
  __decorate([
299
359
  Example(genericAlarmsExample),
300
360
  Get('{id}/alarms'),
361
+ Security('*', ['acl']),
301
362
  Tags('alarms'),
302
363
  Response(notFoundResp.status, notFoundResp.description),
303
364
  __param(0, Request()),
@@ -311,6 +372,8 @@ __decorate([
311
372
  __decorate([
312
373
  Example(hostSmt),
313
374
  Get('{id}/smt'),
375
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
376
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
314
377
  Response(notFoundResp.status, notFoundResp.description),
315
378
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
316
379
  __param(0, Path())
@@ -318,6 +381,8 @@ __decorate([
318
381
  __decorate([
319
382
  Example(hostMissingPatches),
320
383
  Get('{id}/missing_patches'),
384
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
385
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
321
386
  Response(notFoundResp.status, notFoundResp.description),
322
387
  Response(featureUnauthorized.status, featureUnauthorized.description),
323
388
  __param(0, Path())
@@ -326,6 +391,7 @@ __decorate([
326
391
  Example(messageIds),
327
392
  Example(partialMessages),
328
393
  Get('{id}/messages'),
394
+ Security('*', ['acl']),
329
395
  Tags('messages'),
330
396
  Response(notFoundResp.status, notFoundResp.description),
331
397
  __param(0, Request()),
@@ -340,6 +406,7 @@ __decorate([
340
406
  Example(taskIds),
341
407
  Example(partialTasks),
342
408
  Get('{id}/tasks'),
409
+ Security('*', ['acl']),
343
410
  Tags('tasks'),
344
411
  Response(notFoundResp.status, notFoundResp.description),
345
412
  __param(0, Request()),
@@ -352,14 +419,18 @@ __decorate([
352
419
  ], HostController.prototype, "getHostTasks", null);
353
420
  __decorate([
354
421
  Put('{id}/tags/{tag}'),
422
+ Middlewares(acl({ resource: 'host', action: 'update:tags', objectId: 'params.id' })),
355
423
  SuccessResponse(noContentResp.status, noContentResp.description),
424
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
356
425
  Response(notFoundResp.status, notFoundResp.description),
357
426
  __param(0, Path()),
358
427
  __param(1, Path())
359
428
  ], HostController.prototype, "putHostTag", null);
360
429
  __decorate([
361
430
  Delete('{id}/tags/{tag}'),
431
+ Middlewares(acl({ resource: 'host', action: 'update:tags', objectId: 'params.id' })),
362
432
  SuccessResponse(noContentResp.status, noContentResp.description),
433
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
363
434
  Response(notFoundResp.status, notFoundResp.description),
364
435
  __param(0, Path()),
365
436
  __param(1, Path())
package/dist/ioc/ioc.mjs CHANGED
@@ -13,10 +13,11 @@ import { BackupJobService } from '../backup-jobs/backup-job.service.mjs';
13
13
  import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
14
14
  import { EventService } from '../events/event.service.mjs';
15
15
  import { NetworkService } from '../networks/network.service.mjs';
16
+ import { BackupArchiveService } from '../backup-archives/backup-archive.service.mjs';
16
17
  const iocContainer = new Container();
17
- decorate(injectable(), Controller);
18
- iocContainer.load(buildProviderModule());
19
18
  export function setupContainer(xoApp) {
19
+ decorate(injectable(), Controller);
20
+ iocContainer.load(buildProviderModule());
20
21
  if (iocContainer.isBound(RestApi)) {
21
22
  iocContainer.unbind(RestApi);
22
23
  }
@@ -82,8 +83,9 @@ export function setupContainer(xoApp) {
82
83
  .inSingletonScope();
83
84
  iocContainer
84
85
  .bind(BackupLogService)
85
- .toDynamicValue(() => {
86
- return new BackupLogService();
86
+ .toDynamicValue(ctx => {
87
+ const restApi = ctx.container.get(RestApi);
88
+ return new BackupLogService(restApi);
87
89
  })
88
90
  .inSingletonScope();
89
91
  iocContainer
@@ -100,5 +102,12 @@ export function setupContainer(xoApp) {
100
102
  return new NetworkService(restApi);
101
103
  })
102
104
  .inSingletonScope();
105
+ iocContainer
106
+ .bind(BackupArchiveService)
107
+ .toDynamicValue(ctx => {
108
+ const restApi = ctx.container.get(RestApi);
109
+ return new BackupArchiveService(restApi);
110
+ })
111
+ .inSingletonScope();
103
112
  }
104
113
  export { iocContainer };
@@ -7,16 +7,24 @@ 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 { noSuchObject } from 'xo-common/api-errors.js';
13
13
  import { provide } from 'inversify-binding-decorators';
14
+ import { acl } from '../middlewares/acl.middleware.mjs';
14
15
  import { alarmPredicate } from '../alarms/alarm.service.mjs';
15
16
  import { message, messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
16
17
  import { RestApi } from '../rest-api/rest-api.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 { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
19
20
  import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
21
+ function getMessage(restApi, id) {
22
+ const message = restApi.getObject(id, 'message');
23
+ if (alarmPredicate(message)) {
24
+ throw noSuchObject(id, 'message');
25
+ }
26
+ return message;
27
+ }
20
28
  let MessageController = class MessageController extends XapiXoController {
21
29
  constructor(restapi) {
22
30
  super('message', restapi);
@@ -36,21 +44,26 @@ let MessageController = class MessageController extends XapiXoController {
36
44
  * Override parent getObject in order to exclude`ALARM` message
37
45
  */
38
46
  getObject(id) {
39
- const message = super.getObject(id);
40
- if (alarmPredicate(message)) {
41
- throw noSuchObject(id, 'message');
42
- }
43
- return message;
47
+ return getMessage(this.restApi, id);
44
48
  }
45
49
  /**
50
+ * Returns all messages that match the following privilege:
51
+ * - resource: message, action: read
52
+ *
46
53
  * @example fields "name,body,id,$object"
47
54
  * @example filter "name:VM_STARTED"
48
55
  * @example limit 42
49
56
  */
50
57
  getMessages(req, fields, ndjson, markdown, filter, limit) {
51
- return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
58
+ return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
59
+ limit,
60
+ privilege: { action: 'read', resource: 'message' },
61
+ });
52
62
  }
53
63
  /**
64
+ * Required privilege:
65
+ * - resource: message, action: read
66
+ *
54
67
  * @example id "f775eaeb-abe5-94e0-9682-14c37c3a1dfe"
55
68
  */
56
69
  getMessage(id) {
@@ -61,6 +74,7 @@ __decorate([
61
74
  Example(messageIds),
62
75
  Example(partialMessages),
63
76
  Get(''),
77
+ Security('*', ['acl']),
64
78
  __param(0, Request()),
65
79
  __param(1, Query()),
66
80
  __param(2, Query()),
@@ -71,6 +85,13 @@ __decorate([
71
85
  __decorate([
72
86
  Example(message),
73
87
  Get('{id}'),
88
+ Middlewares(acl({
89
+ resource: 'message',
90
+ action: 'read',
91
+ objectId: 'params.id',
92
+ getObject: ({ restApi }) => id => getMessage(restApi, id),
93
+ })),
94
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
74
95
  Response(notFoundResp.status, notFoundResp.description),
75
96
  __param(0, Path())
76
97
  ], MessageController.prototype, "getMessage", null);