diff --git a/@xen-orchestra/acl/src/actions/vif.mts b/@xen-orchestra/acl/src/actions/vif.mts index e6f420ef10..523e28e32d 100644 --- a/@xen-orchestra/acl/src/actions/vif.mts +++ b/@xen-orchestra/acl/src/actions/vif.mts @@ -1,4 +1,7 @@ export default { - read: true, + connect: true, create: true, + delete: true, + disconnect: true, + read: true, } diff --git a/@xen-orchestra/rest-api/src/vifs/vif.controller.mts b/@xen-orchestra/rest-api/src/vifs/vif.controller.mts index 4c37963441..39b00f8aea 100644 --- a/@xen-orchestra/rest-api/src/vifs/vif.controller.mts +++ b/@xen-orchestra/rest-api/src/vifs/vif.controller.mts @@ -227,12 +227,15 @@ export class VifController extends XapiXoController { } /** + * Required privilege: + * - resource: vif, action: create + * * @example body { * "networkId": "6b6ca0f5-6611-0636-4b0a-1fb1c1e96414", * "vmId": "613f541c-4bed-fc77-7ca8-2db6b68f079c", - * "other_config": { - *"ethtool-tx": "false" - * }, + * "other_config": { + * "ethtool-tx": "false" + * }, * "qos_algorithm_params": { * "kbps": "42" * }, @@ -242,9 +245,10 @@ export class VifController extends XapiXoController { @Example(vifId) @Extension('x-mcp-exposure', 'confirm') @Post('') - @Middlewares(json()) + @Middlewares([json(), acl({ resource: 'vif', action: 'create', object: ({ req }) => req.body })]) @SuccessResponse(createdResp.status, createdResp.description) @Response(notFoundResp.status, notFoundResp.description) + @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(internalServerErrorResp.status, internalServerErrorResp.description) @Response(invalidParametersResp.status, invalidParametersResp.description) async createVif( @@ -277,11 +281,16 @@ export class VifController extends XapiXoController { } /** + * Required privilege: + * - resource: vif, action: delete + * * @example id "6b6ca0f5-6611-0636-4b0a-1fb1c1e96414" */ @Extension('x-mcp-exposure', 'confirm') @Delete('{id}') @SuccessResponse(noContentResp.status, noContentResp.description) + @Middlewares(acl({ resource: 'vif', action: 'delete', objectId: 'params.id' })) + @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @Response(internalServerErrorResp.status, internalServerErrorResp.description) async destroyVif(@Path() id: string): Promise { @@ -293,13 +302,18 @@ export class VifController extends XapiXoController { * Hotplug the VIF, dynamically attaching it to the running VM * Requires PV drivers to be installed on the VM * + * Required privilege: + * - resource: vif, action: connect + * * @example id "f07ab729-c0e8-721c-45ec-f11276377030" */ @Example(taskLocation) @Extension('x-mcp-exposure', 'confirm') + @Middlewares(acl({ resource: 'vif', action: 'connect', objectId: 'params.id' })) @Post('{id}/actions/connect') @SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description) @Response(noContentResp.status, noContentResp.description) + @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @Response(internalServerErrorResp.status, internalServerErrorResp.description) async connectVif(@Path() id: string, @Query() sync?: boolean): CreateActionReturnType { @@ -324,13 +338,18 @@ export class VifController extends XapiXoController { * Hot-unplug the VIF, dynamically detaching it from the running VM * Requires PV drivers to be installed on the VM * + * Required privilege: + * - resource: vif, action: disconnect + * * @example id "f07ab729-c0e8-721c-45ec-f11276377030" */ @Example(taskLocation) @Extension('x-mcp-exposure', 'confirm') @Post('{id}/actions/disconnect') + @Middlewares(acl({ resource: 'vif', action: 'disconnect', objectId: 'params.id' })) @SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description) @Response(noContentResp.status, noContentResp.description) + @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @Response(internalServerErrorResp.status, internalServerErrorResp.description) async disconnectVif(@Path() id: string, @Query() sync?: boolean): CreateActionReturnType { diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 5844ddea2e..1fd4c21b7a 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -15,8 +15,12 @@ - **RBAC** check for REST API endpoints: - `POST /vbds` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) - `DELETE /vbds/:id` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) - - `vbds/:id/actions/connect` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) - - `vbds/:id/actions/disconnect` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) + - `POST vbds/:id/actions/connect` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) + - `POST vbds/:id/actions/disconnect` (PR [#9904](https://github.com/vatesfr/xen-orchestra/pull/9904)) + - `POST /vifs` (PR [#9889](https://github.com/vatesfr/xen-orchestra/pull/9889)) + - `DELETE /vifs/:id` (PR [#9889](https://github.com/vatesfr/xen-orchestra/pull/9889)) + - `POST /vifs/:id/actions/connect` (PR [#9889](https://github.com/vatesfr/xen-orchestra/pull/9889)) + - `POST /vifs/:id/actions/disconnect` (PR [#9889](https://github.com/vatesfr/xen-orchestra/pull/9889)) ### Bug fixes diff --git a/docs/docs/xo6/acl-v2.md b/docs/docs/xo6/acl-v2.md index 3d81c14436..599e691827 100644 --- a/docs/docs/xo6/acl-v2.md +++ b/docs/docs/xo6/acl-v2.md @@ -113,7 +113,7 @@ Actions are written using the exact string you pass in a privilege. A parent act | `vdi` | `read`, `create`, `delete`, `boot`, `export-content`, `import-content`, `update:tags` | | `vdi-snapshot` | `read` | | `vdi-unmanaged` | `read` | -| `vif` | `read`, `create` | +| `vif` | `connect`, `create`, `delete`, `disconnect`,`read` | | `vbd` | `read`, `create`, `delete`, `connect`, `disconnect` | | `sr` | `read`, `delete`, `import:vdi`, `import:vm`, `update:tags` | | `host` | `read`, `allow-vm`, `export:logs`, `update:tags`, `disable`, `enable`, `evacuate` |