@xen-orchestra/rest-api 0.28.2 → 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 (55) hide show
  1. package/README.md +108 -1
  2. package/dist/abstract-classes/base-controller.mjs +28 -3
  3. package/dist/abstract-classes/listener.mjs +124 -15
  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 +25 -11
  7. package/dist/alarms/alarm.service.mjs +8 -0
  8. package/dist/backup-archives/backup-archive.controller.mjs +33 -23
  9. package/dist/backup-archives/backup-archive.service.mjs +21 -0
  10. package/dist/backup-jobs/backup-job.controller.mjs +74 -25
  11. package/dist/backup-jobs/backup-job.service.mjs +7 -0
  12. package/dist/backup-logs/backup-log.controller.mjs +28 -13
  13. package/dist/backup-logs/backup-log.service.mjs +19 -0
  14. package/dist/backup-repositories/backup-repositories.controller.mjs +24 -5
  15. package/dist/events/event.class.mjs +36 -18
  16. package/dist/events/event.controller.mjs +3 -0
  17. package/dist/events/event.service.mjs +4 -4
  18. package/dist/groups/group.controller.mjs +99 -12
  19. package/dist/helpers/markdown.helper.mjs +20 -0
  20. package/dist/helpers/object-wrapper.helper.mjs +3 -3
  21. package/dist/hosts/host.controller.mjs +90 -15
  22. package/dist/ioc/ioc.mjs +13 -4
  23. package/dist/messages/message.controller.mjs +32 -10
  24. package/dist/middlewares/acl.middleware.mjs +202 -0
  25. package/dist/middlewares/authentication.middleware.mjs +15 -6
  26. package/dist/middlewares/tsoa-to-xo-error.middleware.mjs +19 -1
  27. package/dist/networks/network.controller.mjs +72 -17
  28. package/dist/open-api/oa-examples/acl-privilege.oa-example.mjs +25 -0
  29. package/dist/open-api/oa-examples/acl-role.oa-example.mjs +22 -0
  30. package/dist/open-api/oa-examples/backup-archive.oa-example.mjs +6 -6
  31. package/dist/open-api/oa-examples/common.oa-example.mjs +3 -0
  32. package/dist/open-api/routes/routes.js +856 -172
  33. package/dist/pbds/pbd.controller.mjs +20 -5
  34. package/dist/pcis/pci.controller.mjs +19 -5
  35. package/dist/pgpus/pgpu.controller.mjs +19 -5
  36. package/dist/pifs/pif.controller.mjs +56 -16
  37. package/dist/pools/pool.controller.mjs +166 -17
  38. package/dist/proxies/proxy.controller.mjs +25 -6
  39. package/dist/restore-logs/restore-log.controller.mjs +42 -23
  40. package/dist/schedules/schedule.controller.mjs +36 -5
  41. package/dist/servers/server.controller.mjs +71 -9
  42. package/dist/sms/sm.controller.mjs +17 -4
  43. package/dist/srs/sr.controller.mjs +74 -18
  44. package/dist/tasks/task.controller.mjs +74 -13
  45. package/dist/users/user.controller.mjs +124 -22
  46. package/dist/vbds/vbd.controller.mjs +76 -38
  47. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +48 -14
  48. package/dist/vdis/vdi.controller.mjs +81 -16
  49. package/dist/vifs/vif.controller.mjs +118 -16
  50. package/dist/vm-controller/vm-controller.controller.mjs +77 -19
  51. package/dist/vm-snapshots/vm-snapshot.controller.mjs +85 -18
  52. package/dist/vm-templates/vm-template.controller.mjs +86 -18
  53. package/dist/vms/vm.controller.mjs +182 -24
  54. package/open-api/spec/swagger.json +12112 -3537
  55. package/package.json +12 -11
@@ -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
- async getGroups(req, fields, ndjson, filter, limit) {
42
- return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req);
45
+ async getGroups(req, fields, ndjson, markdown, filter, limit) {
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,46 +129,77 @@ 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"
103
138
  * @example limit 42
104
139
  */
105
- async getGroupUsers(req, id, fields, ndjson, filter, limit) {
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
- async getGroupTasks(req, id, fields, ndjson, filter, limit) {
117
- const tasks = await this.getTasksForObject(id, { filter, limit });
118
- return this.sendObjects(Object.values(tasks), req, 'tasks');
158
+ async getGroupTasks(req, id, fields, ndjson, markdown, filter, limit) {
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()),
128
175
  __param(3, Query()),
129
- __param(4, Query())
176
+ __param(4, Query()),
177
+ __param(5, Query())
130
178
  ], GroupController.prototype, "getGroups", null);
131
179
  __decorate([
132
180
  Example(group),
133
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),
134
189
  Response(notFoundResp.status, notFoundResp.description),
135
190
  __param(0, Path())
136
191
  ], GroupController.prototype, "getGroup", null);
137
192
  __decorate([
138
193
  Patch('{id}'),
139
- 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
+ ]),
140
203
  SuccessResponse(noContentResp.status, noContentResp.description),
141
204
  Response(notFoundResp.status, notFoundResp.description),
142
205
  Response(resourceAlreadyExists.status, resourceAlreadyExists.description),
@@ -147,20 +210,34 @@ __decorate([
147
210
  __decorate([
148
211
  Example(groupId),
149
212
  Post(''),
150
- Middlewares(json()),
213
+ Middlewares([json(), acl({ resource: 'group', action: 'create', object: ({ req }) => req.body })]),
151
214
  SuccessResponse(createdResp.status, createdResp.description),
215
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
152
216
  Response(invalidParameters.status, invalidParameters.description),
153
217
  Response(resourceAlreadyExists.status, resourceAlreadyExists.description),
154
218
  __param(0, Body())
155
219
  ], GroupController.prototype, "createGroup", null);
156
220
  __decorate([
157
221
  Delete('{id}'),
222
+ Middlewares(acl({
223
+ resource: 'group',
224
+ action: 'delete',
225
+ objectId: 'params.id',
226
+ getObject: ({ restApi }) => restApi.xoApp.getGroup,
227
+ })),
158
228
  SuccessResponse(noContentResp.status, noContentResp.description),
229
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
159
230
  Response(notFoundResp.status, notFoundResp.description),
160
231
  __param(0, Path())
161
232
  ], GroupController.prototype, "deleteGroup", null);
162
233
  __decorate([
163
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
+ })),
164
241
  SuccessResponse(noContentResp.status, noContentResp.description),
165
242
  Response(notFoundResp.status, notFoundResp.description),
166
243
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -169,6 +246,12 @@ __decorate([
169
246
  ], GroupController.prototype, "removeUserFromGroup", null);
170
247
  __decorate([
171
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
+ })),
172
255
  SuccessResponse(noContentResp.status, noContentResp.description),
173
256
  Response(notFoundResp.status, notFoundResp.description),
174
257
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -179,6 +262,7 @@ __decorate([
179
262
  Example(userIds),
180
263
  Example(partialUsers),
181
264
  Get('{id}/users'),
265
+ Security('*', ['acl']),
182
266
  Tags('users'),
183
267
  Response(notFoundResp.status, notFoundResp.description),
184
268
  __param(0, Request()),
@@ -186,12 +270,14 @@ __decorate([
186
270
  __param(2, Query()),
187
271
  __param(3, Query()),
188
272
  __param(4, Query()),
189
- __param(5, Query())
273
+ __param(5, Query()),
274
+ __param(6, Query())
190
275
  ], GroupController.prototype, "getGroupUsers", null);
191
276
  __decorate([
192
277
  Example(taskIds),
193
278
  Example(partialTasks),
194
279
  Get('{id}/tasks'),
280
+ Security('*', ['acl']),
195
281
  Tags('tasks'),
196
282
  Response(notFoundResp.status, notFoundResp.description),
197
283
  __param(0, Request()),
@@ -199,7 +285,8 @@ __decorate([
199
285
  __param(2, Query()),
200
286
  __param(3, Query()),
201
287
  __param(4, Query()),
202
- __param(5, Query())
288
+ __param(5, Query()),
289
+ __param(6, Query())
203
290
  ], GroupController.prototype, "getGroupTasks", null);
204
291
  GroupController = __decorate([
205
292
  Route('groups'),
@@ -0,0 +1,20 @@
1
+ const escapeCell = (value) => {
2
+ if (value == null)
3
+ return '';
4
+ const text = typeof value === 'object' ? JSON.stringify(value) : String(value);
5
+ return text.replace(/\|/g, '\\|').replace(/\n/g, ' ');
6
+ };
7
+ export function makeMarkdownTable(objects) {
8
+ if (objects.length === 0) {
9
+ return 'No results.';
10
+ }
11
+ if (typeof objects[0] === 'string') {
12
+ return objects.join('\n');
13
+ }
14
+ const records = objects;
15
+ const headers = Object.keys(records[0]).filter(key => key !== 'href');
16
+ const headerRow = '| ' + headers.join(' | ') + ' |';
17
+ const separatorRow = '| ' + headers.map(() => '---').join(' | ') + ' |';
18
+ const dataRows = records.map(obj => '| ' + headers.map(header => escapeCell(obj[header])).join(' | ') + ' |');
19
+ return [headerRow, separatorRow, ...dataRows].join('\n');
20
+ }
@@ -11,11 +11,11 @@ export function makeObjectMapper(req, path) {
11
11
  if (tmpPath.startsWith('/')) {
12
12
  tmpPath = tmpPath.slice(1);
13
13
  }
14
- if (tmpPath.endsWith('/')) {
15
- tmpPath = tmpPath.slice(0, -1);
16
- }
17
14
  _path = `${BASE_URL}/${tmpPath}`;
18
15
  }
16
+ if (_path.endsWith('/')) {
17
+ _path = _path.slice(0, -1);
18
+ }
19
19
  return `${_path}/${String(obj.id)}`;
20
20
  };
21
21
  let objectMapper;
@@ -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
- getHosts(req, fields, ndjson, filter, limit) {
42
- return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
45
+ getHosts(req, fields, ndjson, markdown, filter, limit) {
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,20 +109,29 @@ 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"
96
118
  * @example limit 42
97
119
  */
98
- getHostAlarms(req, id, fields, ndjson, filter, limit) {
120
+ getHostAlarms(req, id, fields, ndjson, markdown, filter, limit) {
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
- getHostMessages(req, id, fields, ndjson, filter, limit) {
133
- const messages = this.getMessagesForObject(id, { filter, limit });
134
- return this.sendObjects(Object.values(messages), req, 'messages');
166
+ getHostMessages(req, id, fields, ndjson, markdown, filter, limit) {
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
- async getHostTasks(req, id, fields, ndjson, filter, limit) {
143
- const tasks = await this.getTasksForObject(id, { filter, limit });
144
- return this.sendObjects(Object.values(tasks), req, 'tasks');
183
+ async getHostTasks(req, id, fields, ndjson, markdown, filter, limit) {
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,21 +308,27 @@ __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()),
263
315
  __param(3, Query()),
264
- __param(4, Query())
316
+ __param(4, Query()),
317
+ __param(5, Query())
265
318
  ], HostController.prototype, "getHosts", null);
266
319
  __decorate([
267
320
  Example(host),
268
321
  Get('{id}'),
322
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
323
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
269
324
  Response(notFoundResp.status, notFoundResp.description),
270
325
  __param(0, Path())
271
326
  ], HostController.prototype, "getHost", null);
272
327
  __decorate([
273
328
  Example(hostStats),
274
329
  Get('{id}/stats'),
330
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
331
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
275
332
  Response(notFoundResp.status, notFoundResp.description),
276
333
  Response(422, 'Invalid granularity'),
277
334
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
@@ -280,7 +337,9 @@ __decorate([
280
337
  ], HostController.prototype, "getHostStats", null);
281
338
  __decorate([
282
339
  Get('{id}/audit.txt'),
340
+ Middlewares(acl({ resource: 'host', action: 'export:logs', objectId: 'params.id' })),
283
341
  SuccessResponse(200, 'Download started', 'application/octet-stream'),
342
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
284
343
  Response(notFoundResp.status, notFoundResp.description),
285
344
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
286
345
  __param(0, Request()),
@@ -288,7 +347,9 @@ __decorate([
288
347
  ], HostController.prototype, "getAuditLog", null);
289
348
  __decorate([
290
349
  Get('{id}/logs.tgz'),
350
+ Middlewares(acl({ resource: 'host', action: 'export:logs', objectId: 'params.id' })),
291
351
  SuccessResponse(200, 'Download started', 'application/gzip'),
352
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
292
353
  Response(notFoundResp.status, notFoundResp.description),
293
354
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
294
355
  __param(0, Request()),
@@ -297,6 +358,7 @@ __decorate([
297
358
  __decorate([
298
359
  Example(genericAlarmsExample),
299
360
  Get('{id}/alarms'),
361
+ Security('*', ['acl']),
300
362
  Tags('alarms'),
301
363
  Response(notFoundResp.status, notFoundResp.description),
302
364
  __param(0, Request()),
@@ -304,11 +366,14 @@ __decorate([
304
366
  __param(2, Query()),
305
367
  __param(3, Query()),
306
368
  __param(4, Query()),
307
- __param(5, Query())
369
+ __param(5, Query()),
370
+ __param(6, Query())
308
371
  ], HostController.prototype, "getHostAlarms", null);
309
372
  __decorate([
310
373
  Example(hostSmt),
311
374
  Get('{id}/smt'),
375
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
376
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
312
377
  Response(notFoundResp.status, notFoundResp.description),
313
378
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
314
379
  __param(0, Path())
@@ -316,6 +381,8 @@ __decorate([
316
381
  __decorate([
317
382
  Example(hostMissingPatches),
318
383
  Get('{id}/missing_patches'),
384
+ Middlewares(acl({ resource: 'host', action: 'read', objectId: 'params.id' })),
385
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
319
386
  Response(notFoundResp.status, notFoundResp.description),
320
387
  Response(featureUnauthorized.status, featureUnauthorized.description),
321
388
  __param(0, Path())
@@ -324,6 +391,7 @@ __decorate([
324
391
  Example(messageIds),
325
392
  Example(partialMessages),
326
393
  Get('{id}/messages'),
394
+ Security('*', ['acl']),
327
395
  Tags('messages'),
328
396
  Response(notFoundResp.status, notFoundResp.description),
329
397
  __param(0, Request()),
@@ -331,12 +399,14 @@ __decorate([
331
399
  __param(2, Query()),
332
400
  __param(3, Query()),
333
401
  __param(4, Query()),
334
- __param(5, Query())
402
+ __param(5, Query()),
403
+ __param(6, Query())
335
404
  ], HostController.prototype, "getHostMessages", null);
336
405
  __decorate([
337
406
  Example(taskIds),
338
407
  Example(partialTasks),
339
408
  Get('{id}/tasks'),
409
+ Security('*', ['acl']),
340
410
  Tags('tasks'),
341
411
  Response(notFoundResp.status, notFoundResp.description),
342
412
  __param(0, Request()),
@@ -344,18 +414,23 @@ __decorate([
344
414
  __param(2, Query()),
345
415
  __param(3, Query()),
346
416
  __param(4, Query()),
347
- __param(5, Query())
417
+ __param(5, Query()),
418
+ __param(6, Query())
348
419
  ], HostController.prototype, "getHostTasks", null);
349
420
  __decorate([
350
421
  Put('{id}/tags/{tag}'),
422
+ Middlewares(acl({ resource: 'host', action: 'update:tags', objectId: 'params.id' })),
351
423
  SuccessResponse(noContentResp.status, noContentResp.description),
424
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
352
425
  Response(notFoundResp.status, notFoundResp.description),
353
426
  __param(0, Path()),
354
427
  __param(1, Path())
355
428
  ], HostController.prototype, "putHostTag", null);
356
429
  __decorate([
357
430
  Delete('{id}/tags/{tag}'),
431
+ Middlewares(acl({ resource: 'host', action: 'update:tags', objectId: 'params.id' })),
358
432
  SuccessResponse(noContentResp.status, noContentResp.description),
433
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
359
434
  Response(notFoundResp.status, notFoundResp.description),
360
435
  __param(0, Path()),
361
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 };