@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.
Files changed (53) hide show
  1. package/README.md +101 -1
  2. package/dist/abstract-classes/base-controller.mjs +20 -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 +206 -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 +75 -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
@@ -11,7 +11,8 @@ import { Body, Delete, Example, Get, Middlewares, Path, Post, Query, Request, Re
11
11
  import { json } from 'express';
12
12
  import { inject } from 'inversify';
13
13
  import { provide } from 'inversify-binding-decorators';
14
- import { asynchronousActionResp, badRequestResp, createdResp, invalidParameters, noContentResp, notFoundResp, resourceAlreadyExists, unauthorizedResp, } from '../open-api/common/response.common.mjs';
14
+ import { acl } from '../middlewares/acl.middleware.mjs';
15
+ import { asynchronousActionResp, badRequestResp, createdResp, forbiddenOperationResp, invalidParameters, noContentResp, notFoundResp, resourceAlreadyExists, unauthorizedResp, } from '../open-api/common/response.common.mjs';
15
16
  import { partialServers, server, serverId, serverIds } from '../open-api/oa-examples/server.oa-example.mjs';
16
17
  import { partialTasks, taskIds, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
17
18
  import { XoController } from '../abstract-classes/xo-controller.mjs';
@@ -28,26 +29,41 @@ let ServerController = class ServerController extends XoController {
28
29
  return this.restApi.xoApp.getXenServer(id);
29
30
  }
30
31
  /**
32
+ * Returns all servers that match the following privilege:
33
+ * - resource: server, action: read
34
+ *
31
35
  * @example fields "status,id"
32
36
  * @example filter "status:/^connected$/"
33
37
  * @example limit 42
34
38
  */
35
39
  async getServers(req, fields, ndjson, markdown, filter, limit) {
36
- return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req);
40
+ return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
41
+ limit,
42
+ privilege: { action: 'read', resource: 'server' },
43
+ });
37
44
  }
38
45
  /**
46
+ * Required privilege:
47
+ * - resource: server, action: read
48
+ *
39
49
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
40
50
  */
41
51
  getServer(id) {
42
52
  return this.getObject(id);
43
53
  }
44
54
  /**
55
+ * Required privilege:
56
+ * - resource: server, action: delete
57
+ *
45
58
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
46
59
  */
47
60
  async deleteServer(id) {
48
61
  await this.restApi.xoApp.unregisterXenServer(id);
49
62
  }
50
63
  /**
64
+ * Required privilege:
65
+ * - resource: server, action: create
66
+ *
51
67
  * @example body {
52
68
  * "allowUnauthorized": true,
53
69
  * "host": "192.168.1.10",
@@ -61,6 +77,9 @@ let ServerController = class ServerController extends XoController {
61
77
  return { id: server.id };
62
78
  }
63
79
  /**
80
+ * Required privilege:
81
+ * - resource: server, action: connect
82
+ *
64
83
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
65
84
  */
66
85
  connectServer(id, sync) {
@@ -75,6 +94,9 @@ let ServerController = class ServerController extends XoController {
75
94
  });
76
95
  }
77
96
  /**
97
+ * Required privilege:
98
+ * - resource: server, action: disconnect
99
+ *
78
100
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
79
101
  */
80
102
  disconnectServer(id, sync) {
@@ -89,20 +111,28 @@ let ServerController = class ServerController extends XoController {
89
111
  });
90
112
  }
91
113
  /**
114
+ * Returns all tasks that match the following privilege:
115
+ * - resource: task, action: read
116
+ *
92
117
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
93
118
  * @example fields "id,status,properties"
94
119
  * @example filter "status:failure"
95
120
  * @example limit 42
96
121
  */
97
122
  async getServerTasks(req, id, fields, ndjson, markdown, filter, limit) {
98
- const tasks = await this.getTasksForObject(id, { filter, limit });
99
- return this.sendObjects(Object.values(tasks), req, 'tasks');
123
+ const tasks = await this.getTasksForObject(id, { filter });
124
+ return this.sendObjects(Object.values(tasks), req, {
125
+ path: 'tasks',
126
+ limit,
127
+ privilege: { action: 'read', resource: 'task' },
128
+ });
100
129
  }
101
130
  };
102
131
  __decorate([
103
132
  Example(serverIds),
104
133
  Example(partialServers),
105
134
  Get(''),
135
+ Security('*', ['acl']),
106
136
  __param(0, Request()),
107
137
  __param(1, Query()),
108
138
  __param(2, Query()),
@@ -113,20 +143,35 @@ __decorate([
113
143
  __decorate([
114
144
  Example(server),
115
145
  Get('{id}'),
146
+ Middlewares(acl({
147
+ resource: 'server',
148
+ action: 'read',
149
+ objectId: 'params.id',
150
+ getObject: ({ restApi }) => restApi.xoApp.getXenServer,
151
+ })),
152
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
116
153
  Response(notFoundResp.status, notFoundResp.description),
117
154
  __param(0, Path())
118
155
  ], ServerController.prototype, "getServer", null);
119
156
  __decorate([
120
157
  Delete('{id}'),
158
+ Middlewares(acl({
159
+ resource: 'server',
160
+ action: 'delete',
161
+ objectId: 'params.id',
162
+ getObject: ({ restApi }) => restApi.xoApp.getXenServer,
163
+ })),
121
164
  SuccessResponse(noContentResp.status, noContentResp.description),
165
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
122
166
  Response(notFoundResp.status, notFoundResp.description),
123
167
  __param(0, Path())
124
168
  ], ServerController.prototype, "deleteServer", null);
125
169
  __decorate([
126
170
  Example(serverId),
127
171
  Post(''),
128
- Middlewares(json()),
172
+ Middlewares([json(), acl({ resource: 'server', action: 'create', object: ({ req }) => req.body })]),
129
173
  SuccessResponse(createdResp.status, createdResp.description),
174
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
130
175
  Response(resourceAlreadyExists.status, resourceAlreadyExists.description),
131
176
  Response(invalidParameters.status, invalidParameters.description),
132
177
  __param(0, Body())
@@ -134,8 +179,15 @@ __decorate([
134
179
  __decorate([
135
180
  Example(taskLocation),
136
181
  Post('{id}/actions/connect'),
182
+ Middlewares(acl({
183
+ resource: 'server',
184
+ action: 'connect',
185
+ objectId: 'params.id',
186
+ getObject: ({ restApi }) => restApi.xoApp.getXenServer,
187
+ })),
137
188
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
138
189
  Response(noContentResp.status, noContentResp.description),
190
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
139
191
  Response(notFoundResp.status, notFoundResp.description),
140
192
  Response(409, 'The server is already connected'),
141
193
  __param(0, Path()),
@@ -144,8 +196,15 @@ __decorate([
144
196
  __decorate([
145
197
  Example(taskLocation),
146
198
  Post('{id}/actions/disconnect'),
199
+ Middlewares(acl({
200
+ resource: 'server',
201
+ action: 'disconnect',
202
+ objectId: 'params.id',
203
+ getObject: ({ restApi }) => restApi.xoApp.getXenServer,
204
+ })),
147
205
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
148
206
  Response(noContentResp.status, noContentResp.description),
207
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
149
208
  Response(notFoundResp.status, notFoundResp.description),
150
209
  Response(409, 'The server is already disconnected'),
151
210
  __param(0, Path()),
@@ -155,6 +214,7 @@ __decorate([
155
214
  Example(taskIds),
156
215
  Example(partialTasks),
157
216
  Get('{id}/tasks'),
217
+ Security('*', ['acl']),
158
218
  Tags('tasks'),
159
219
  Response(notFoundResp.status, notFoundResp.description),
160
220
  __param(0, Request()),
@@ -7,9 +7,10 @@ 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 { acl } from '../middlewares/acl.middleware.mjs';
13
14
  import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
14
15
  import { partialSms, sm, smIds } from '../open-api/oa-examples/sm.oa-example.mjs';
15
16
  import { RestApi } from '../rest-api/rest-api.mjs';
@@ -19,14 +20,23 @@ let SmController = class SmController extends XapiXoController {
19
20
  super('SM', restApi);
20
21
  }
21
22
  /**
23
+ * Returns all SMs that match the following privilege:
24
+ * - resource: sm, action: read
25
+ *
22
26
  * @example fields "uuid,name_label,SM_type"
23
27
  * @example filter "SM_type:ext"
24
28
  * @example limit 42
25
29
  */
26
30
  getSrs(req, fields, ndjson, markdown, filter, limit) {
27
- return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
31
+ return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
32
+ limit,
33
+ privilege: { action: 'read', resource: 'sm' },
34
+ });
28
35
  }
29
36
  /**
37
+ * Required privilege:
38
+ * - resource: sm, action: read
39
+ *
30
40
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
31
41
  */
32
42
  getSr(id) {
@@ -37,6 +47,7 @@ __decorate([
37
47
  Example(smIds),
38
48
  Example(partialSms),
39
49
  Get(''),
50
+ Security('*', ['acl']),
40
51
  __param(0, Request()),
41
52
  __param(1, Query()),
42
53
  __param(2, Query()),
@@ -47,6 +58,7 @@ __decorate([
47
58
  __decorate([
48
59
  Example(sm),
49
60
  Get('{id}'),
61
+ Middlewares(acl({ resource: 'sm', action: 'read', objectId: 'params.id' })),
50
62
  Response(notFoundResp.status, notFoundResp.description),
51
63
  __param(0, Path())
52
64
  ], SmController.prototype, "getSr", null);
@@ -7,15 +7,16 @@ 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 { Delete, Example, Get, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
10
+ import { Delete, Example, Get, Middlewares, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { SUPPORTED_VDI_FORMAT } from '@vates/types';
14
+ import { acl } from '../middlewares/acl.middleware.mjs';
14
15
  import { AlarmService } from '../alarms/alarm.service.mjs';
15
16
  import { BASE_URL } from '../index.mjs';
16
17
  import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
17
18
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
18
- import { asynchronousActionResp, badRequestResp, createdResp, internalServerErrorResp, invalidParameters as invalidParametersResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
19
+ import { asynchronousActionResp, badRequestResp, createdResp, internalServerErrorResp, invalidParameters as invalidParametersResp, forbiddenOperationResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
19
20
  import { partialSrs, sr, srIds } from '../open-api/oa-examples/sr.oa-example.mjs';
20
21
  import { vdiId } from '../open-api/oa-examples/vdi.oa-example.mjs';
21
22
  import { RestApi } from '../rest-api/rest-api.mjs';
@@ -29,20 +30,32 @@ let SrController = class SrController extends XapiXoController {
29
30
  this.#alarmService = alarmService;
30
31
  }
31
32
  /**
33
+ * Returns all SRs that match the following privilege:
34
+ * - resource: sr, action: read
35
+ *
32
36
  * @example fields "uuid,name_label,allocationStrategy"
33
37
  * @example filter "allocationStrategy:thin"
34
38
  * @example limit 42
35
39
  */
36
40
  getSrs(req, fields, ndjson, markdown, filter, limit) {
37
- return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
41
+ return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
42
+ limit,
43
+ privilege: { action: 'read', resource: 'sr' },
44
+ });
38
45
  }
39
46
  /**
47
+ * Required privilege:
48
+ * - resource: sr, action: read
49
+ *
40
50
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
41
51
  */
42
52
  getSr(id) {
43
53
  return this.getObject(id);
44
54
  }
45
55
  /**
56
+ * Returns all alarms that match the following privilege:
57
+ * - resource: alarm, action: read
58
+ *
46
59
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
47
60
  * @example fields "id,time"
48
61
  * @example filter "time:>1747053793"
@@ -52,12 +65,19 @@ let SrController = class SrController extends XapiXoController {
52
65
  const sr = this.getObject(id);
53
66
  const alarms = this.#alarmService.getAlarms({
54
67
  filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${sr.uuid}`,
68
+ });
69
+ return this.sendObjects(Object.values(alarms), req, {
70
+ path: 'alarms',
55
71
  limit,
72
+ privilege: { action: 'read', resource: 'alarm' },
56
73
  });
57
- return this.sendObjects(Object.values(alarms), req, 'alarms');
58
74
  }
59
75
  /**
60
76
  * Import an exported VDI
77
+ *
78
+ * Required privilege:
79
+ * - resource: sr, action: import:vdi
80
+ *
61
81
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
62
82
  * @example name_label "VDI_foo_import"
63
83
  * @example name_description "VDI imported by the REST API"
@@ -79,26 +99,43 @@ let SrController = class SrController extends XapiXoController {
79
99
  return { id: vdiId };
80
100
  }
81
101
  /**
102
+ * Returns all messages that match the following privilege:
103
+ * - resource: message, action: read
104
+ *
82
105
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
83
106
  * @example fields "name,id,$object"
84
107
  * @example filter "name:VM_STARTED"
85
108
  * @example limit 42
86
109
  */
87
110
  getSrMessages(req, id, fields, ndjson, markdown, filter, limit) {
88
- const messages = this.getMessagesForObject(id, { filter, limit });
89
- return this.sendObjects(Object.values(messages), req, 'messages');
111
+ const messages = this.getMessagesForObject(id, { filter });
112
+ return this.sendObjects(Object.values(messages), req, {
113
+ path: 'messages',
114
+ limit,
115
+ privilege: { action: 'read', resource: 'message' },
116
+ });
90
117
  }
91
118
  /**
119
+ * Returns all tasks that match the following privilege:
120
+ * - resource: task, action: read
121
+ *
92
122
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
93
123
  * @example fields "id,status,properties"
94
124
  * @example filter "status:failure"
95
125
  * @example limit 42
96
126
  */
97
127
  async getSrTasks(req, id, fields, ndjson, markdown, filter, limit) {
98
- const tasks = await this.getTasksForObject(id, { filter, limit });
99
- return this.sendObjects(Object.values(tasks), req, 'tasks');
128
+ const tasks = await this.getTasksForObject(id, { filter });
129
+ return this.sendObjects(Object.values(tasks), req, {
130
+ path: 'tasks',
131
+ limit,
132
+ privilege: { action: 'read', resource: 'task' },
133
+ });
100
134
  }
101
135
  /**
136
+ * Required privilege:
137
+ * - resource: sr, action: update:tags
138
+ *
102
139
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
103
140
  * @example tag "from-rest-api"
104
141
  */
@@ -107,6 +144,9 @@ let SrController = class SrController extends XapiXoController {
107
144
  await sr.$call('add_tags', tag);
108
145
  }
109
146
  /**
147
+ * Required privilege:
148
+ * - resource: sr, action: update:tags
149
+ *
110
150
  * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
111
151
  * @example tag "from-rest-api"
112
152
  */
@@ -173,6 +213,7 @@ __decorate([
173
213
  Example(srIds),
174
214
  Example(partialSrs),
175
215
  Get(''),
216
+ Security('*', ['acl']),
176
217
  __param(0, Request()),
177
218
  __param(1, Query()),
178
219
  __param(2, Query()),
@@ -183,12 +224,15 @@ __decorate([
183
224
  __decorate([
184
225
  Example(sr),
185
226
  Get('{id}'),
227
+ Middlewares(acl({ resource: 'sr', action: 'read', objectId: 'params.id' })),
228
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
186
229
  Response(notFoundResp.status, notFoundResp.description),
187
230
  __param(0, Path())
188
231
  ], SrController.prototype, "getSr", null);
189
232
  __decorate([
190
233
  Example(genericAlarmsExample),
191
234
  Get('{id}/alarms'),
235
+ Security('*', ['acl']),
192
236
  Tags('alarms'),
193
237
  Response(notFoundResp.status, notFoundResp.description),
194
238
  __param(0, Request()),
@@ -202,8 +246,10 @@ __decorate([
202
246
  __decorate([
203
247
  Example(vdiId),
204
248
  Post('{id}/vdis'),
249
+ Middlewares(acl({ resource: 'sr', action: 'import:vdi', objectId: 'params.id' })),
205
250
  Tags('vdis'),
206
251
  SuccessResponse(createdResp.status, 'VDI imported'),
252
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
207
253
  Response(notFoundResp.status, notFoundResp.description),
208
254
  __param(0, Request()),
209
255
  __param(1, Path()),
@@ -215,6 +261,7 @@ __decorate([
215
261
  Example(messageIds),
216
262
  Example(partialMessages),
217
263
  Get('{id}/messages'),
264
+ Security('*', ['acl']),
218
265
  Tags('messages'),
219
266
  Response(notFoundResp.status, notFoundResp.description),
220
267
  __param(0, Request()),
@@ -229,6 +276,7 @@ __decorate([
229
276
  Example(taskIds),
230
277
  Example(partialTasks),
231
278
  Get('{id}/tasks'),
279
+ Security('*', ['acl']),
232
280
  Tags('tasks'),
233
281
  Response(notFoundResp.status, notFoundResp.description),
234
282
  __param(0, Request()),
@@ -240,16 +288,20 @@ __decorate([
240
288
  __param(6, Query())
241
289
  ], SrController.prototype, "getSrTasks", null);
242
290
  __decorate([
291
+ Put('{id}/tags/{tag}'),
292
+ Middlewares(acl({ resource: 'sr', action: 'update:tags', objectId: 'params.id' })),
243
293
  SuccessResponse(noContentResp.status, noContentResp.description),
294
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
244
295
  Response(notFoundResp.status, notFoundResp.description),
245
- Put('{id}/tags/{tag}'),
246
296
  __param(0, Path()),
247
297
  __param(1, Path())
248
298
  ], SrController.prototype, "putSrTag", null);
249
299
  __decorate([
300
+ Delete('{id}/tags/{tag}'),
301
+ Middlewares(acl({ resource: 'sr', action: 'update:tags', objectId: 'params.id' })),
250
302
  SuccessResponse(noContentResp.status, noContentResp.description),
303
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
251
304
  Response(notFoundResp.status, notFoundResp.description),
252
- Delete('{id}/tags/{tag}'),
253
305
  __param(0, Path()),
254
306
  __param(1, Path())
255
307
  ], SrController.prototype, "deleteSrTag", null);
@@ -8,8 +8,9 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { XoController } from '../abstract-classes/xo-controller.mjs';
11
- import { Get, Path, Query, Request, Route, Security, Tags, Response, Example, Delete, Post, SuccessResponse, } from 'tsoa';
12
- import { asynchronousActionResp, badRequestResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
11
+ import { Get, Path, Query, Request, Route, Security, Tags, Response, Example, Delete, Post, SuccessResponse, Middlewares, } from 'tsoa';
12
+ import { acl } from '../middlewares/acl.middleware.mjs';
13
+ import { asynchronousActionResp, badRequestResp, forbiddenOperationResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
13
14
  import { inject } from 'inversify';
14
15
  import { provide } from 'inversify-binding-decorators';
15
16
  import { partialTasks, task, taskIds, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
@@ -19,6 +20,7 @@ import { Transform } from 'node:stream';
19
20
  import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs';
20
21
  import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
21
22
  import { RestApi } from '../rest-api/rest-api.mjs';
23
+ import { hasPrivilegeOn } from '@xen-orchestra/acl';
22
24
  let TaskController = class TaskController extends XoController {
23
25
  constructor(restApi) {
24
26
  super('task', restApi);
@@ -34,6 +36,8 @@ let TaskController = class TaskController extends XoController {
34
36
  return this.restApi.tasks.get(id);
35
37
  }
36
38
  /**
39
+ * Returns all tasks that match the following privilege:
40
+ * - resource: task, action: read
37
41
  *
38
42
  * If watch is true, ndjson must also be true
39
43
  *
@@ -63,21 +67,40 @@ let TaskController = class TaskController extends XoController {
63
67
  process.on('SIGTERM', () => {
64
68
  req.destroy();
65
69
  });
66
- function update(task) {
67
- if (userFilter === undefined || userFilter(task)) {
70
+ const userId = this.restApi.getCurrentUser().id;
71
+ const update = async (task) => {
72
+ const user = await this.restApi.xoApp.getUser(userId);
73
+ const userPrivileges = user.permission === 'admin'
74
+ ? []
75
+ : (await this.restApi.xoApp.getAclV2UserPrivileges(user.id));
76
+ if (hasPrivilegeOn({ user, userPrivileges, action: 'read', resource: 'task', objects: task }) &&
77
+ (userFilter === undefined || userFilter(task))) {
68
78
  stream.write(['update', task]);
69
79
  }
70
- }
71
- function remove(task) {
72
- stream.write(['remove', { id: task.id }]);
73
- }
80
+ };
81
+ const remove = async (task) => {
82
+ const user = await this.restApi.xoApp.getUser(userId);
83
+ const userPrivileges = user.permission === 'admin'
84
+ ? []
85
+ : (await this.restApi.xoApp.getAclV2UserPrivileges(user.id));
86
+ if (hasPrivilegeOn({ user, userPrivileges, action: 'read', resource: 'task', objects: task }) &&
87
+ (userFilter === undefined || userFilter(task))) {
88
+ stream.write(['remove', { id: task.id }]);
89
+ }
90
+ };
74
91
  this.restApi.tasks.on('update', update).on('remove', remove);
75
92
  return stream;
76
93
  }
77
- const tasks = Object.values(await this.getObjects({ filter, limit }));
78
- return this.sendObjects(tasks, req);
94
+ const tasks = Object.values(await this.getObjects({ filter }));
95
+ return this.sendObjects(tasks, req, {
96
+ limit,
97
+ privilege: { action: 'read', resource: 'task' },
98
+ });
79
99
  }
80
100
  /**
101
+ * Required privilege:
102
+ * - resource: task, action: read
103
+ *
81
104
  * @example id "0mdd1basu"
82
105
  */
83
106
  async getTask(req, id, wait) {
@@ -95,10 +118,25 @@ let TaskController = class TaskController extends XoController {
95
118
  }
96
119
  return this.getObject(taskId);
97
120
  }
121
+ /**
122
+ * Deletes all tasks the current user has the following privilege on:
123
+ * - resource: task, action: delete
124
+ */
98
125
  async deleteTasks() {
99
- await this.restApi.tasks.clearLogs();
126
+ const user = this.restApi.getCurrentUser();
127
+ const userPrivileges = user.permission === 'admin' ? [] : (await this.restApi.xoApp.getAclV2UserPrivileges(user.id));
128
+ const deletePromises = [];
129
+ for await (const task of this.restApi.tasks.list()) {
130
+ if (hasPrivilegeOn({ user, userPrivileges, resource: 'task', action: 'delete', objects: task })) {
131
+ deletePromises.push(this.restApi.tasks.deleteLog(task.id));
132
+ }
133
+ }
134
+ await Promise.all(deletePromises);
100
135
  }
101
136
  /**
137
+ * Required privilege:
138
+ * - resource: task, action: delete
139
+ *
102
140
  * @example id "0mdd1basu"
103
141
  */
104
142
  async deleteTask(id) {
@@ -106,6 +144,9 @@ let TaskController = class TaskController extends XoController {
106
144
  await this.restApi.tasks.deleteLog(task.id);
107
145
  }
108
146
  /**
147
+ * Required privilege:
148
+ * - resource: task, action: abort
149
+ *
109
150
  * @example id "0mdd1basu"
110
151
  */
111
152
  async abortTask(id, sync) {
@@ -127,6 +168,7 @@ __decorate([
127
168
  Example(taskIds),
128
169
  Example(partialTasks),
129
170
  Get(''),
171
+ Security('*', ['acl']),
130
172
  Response(badRequestResp.status, badRequestResp.description),
131
173
  __param(0, Request()),
132
174
  __param(1, Query()),
@@ -139,6 +181,13 @@ __decorate([
139
181
  __decorate([
140
182
  Example(task),
141
183
  Get('{id}'),
184
+ Middlewares(acl({
185
+ resource: 'task',
186
+ action: 'read',
187
+ objectId: 'params.id',
188
+ getObject: ({ restApi }) => restApi.xoApp.tasks.get.bind(restApi.xoApp.tasks),
189
+ })),
190
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
142
191
  Response(notFoundResp.status, notFoundResp.description),
143
192
  __param(0, Request()),
144
193
  __param(1, Path()),
@@ -146,19 +195,34 @@ __decorate([
146
195
  ], TaskController.prototype, "getTask", null);
147
196
  __decorate([
148
197
  Delete(''),
198
+ Security('*', ['acl']),
149
199
  SuccessResponse(noContentResp.status, noContentResp.description)
150
200
  ], TaskController.prototype, "deleteTasks", null);
151
201
  __decorate([
152
202
  Delete('{id}'),
203
+ Middlewares(acl({
204
+ resource: 'task',
205
+ action: 'delete',
206
+ objectId: 'params.id',
207
+ getObject: ({ restApi }) => restApi.xoApp.tasks.get.bind(restApi.xoApp.tasks),
208
+ })),
153
209
  SuccessResponse(noContentResp.status, noContentResp.description),
210
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
154
211
  Response(notFoundResp.status, notFoundResp.description),
155
212
  __param(0, Path())
156
213
  ], TaskController.prototype, "deleteTask", null);
157
214
  __decorate([
158
215
  Example(taskLocation),
159
216
  Post('{id}/actions/abort'),
217
+ Middlewares(acl({
218
+ resource: 'task',
219
+ action: 'abort',
220
+ objectId: 'params.id',
221
+ getObject: ({ restApi }) => restApi.xoApp.tasks.get.bind(restApi.xoApp.tasks),
222
+ })),
160
223
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
161
224
  Response(noContentResp.status, noContentResp.description),
225
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
162
226
  Response(notFoundResp.status, notFoundResp.description),
163
227
  __param(0, Path()),
164
228
  __param(1, Query())