mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-12 23:19:48 -05:00
feat(xo-server): add groupIds and userIds to acl-role object
This commit is contained in:
@@ -1,19 +1,24 @@
|
||||
import { Branded } from '../common.mjs'
|
||||
import type { XoGroup, XoUser } from '../xo.mjs'
|
||||
|
||||
export type XoAclRole =
|
||||
| {
|
||||
id: Branded<'acl-v2-role'>
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
| {
|
||||
id: Branded<'acl-v2-role'>
|
||||
name: string
|
||||
description?: string
|
||||
isTemplate: true
|
||||
roleTemplateId: number
|
||||
}
|
||||
type XoAclBaseRole = {
|
||||
id: Branded<'acl-v2-role'>
|
||||
name: string
|
||||
description?: string
|
||||
privilegeIds: XoAclBasePrivilege['id'][]
|
||||
}
|
||||
|
||||
type XoAclRegularRole = XoAclBaseRole & {
|
||||
groupIds: XoGroup['id'][]
|
||||
userIds: XoUser['id'][]
|
||||
}
|
||||
|
||||
type XoAclTemplateRole = XoAclBaseRole & {
|
||||
isTemplate: true
|
||||
roleTemplateId: number
|
||||
}
|
||||
|
||||
export type XoAclRole = XoAclRegularRole | XoAclTemplateRole
|
||||
|
||||
export type XoAclSupportedActionsByResource = {
|
||||
[resource: string]: Record<string, unknown>
|
||||
|
||||
@@ -21,4 +21,19 @@ export const aclRole = {
|
||||
name: 'mra-test-read-only',
|
||||
description: 'Access the whole infra in read-only mode',
|
||||
id: '784bd959-08de-4b26-b575-92ded5aef872',
|
||||
userIds: ['6b017bcf-99e4-40a2-bf64-bf723d6c1994'],
|
||||
groupIds: [],
|
||||
privilegeIds: [
|
||||
'fe6c806d-c993-4144-9018-d3d0c057fbc1',
|
||||
'a4e0c8a4-0fe7-47e4-a630-06707b10e672',
|
||||
'bfce6c26-966c-4a59-a9ca-e11e66139850',
|
||||
'e759f285-de67-44bf-b030-158211b25504',
|
||||
'b086d21e-7fbd-46e7-9ae6-60ad77c905eb',
|
||||
'88d49d33-319a-4af2-866b-1835cda254e6',
|
||||
'bd59c0bc-2db5-4ccf-a50c-8be94e43324a',
|
||||
'9e3376ef-cc54-4480-b7b1-cc8b802ed8c0',
|
||||
'59026b9b-0f07-4735-92b1-fd98fb95306c',
|
||||
'e395bfc9-747c-43a5-a46d-6abd01a0e96e',
|
||||
'69fd4212-e0d5-4bac-b453-08377aba48f5',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
- [Pool] Add new VM and disconnect actions to the pool infrastructure tree (PR [#10046](https://github.com/vatesfr/xen-orchestra/pull/10046))
|
||||
- [REST API] Expose `/rest/v0/groups/:id/acl-roles` (PR [#10085](https://github.com/vatesfr/xen-orchestra/pull/10085))
|
||||
- [XO server] Add `aclRoleIds` property to the `group` objects (PR [#10085](https://github.com/vatesfr/xen-orchestra/pull/10085))
|
||||
- [XO server] Add `groupIds`, `userIds` and `privilegeIds` properties to the `acl-role` objects (PR [#10085](https://github.com/vatesfr/xen-orchestra/pull/10085))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
||||
@@ -236,6 +236,21 @@ export default class {
|
||||
return this.#roleDb.add({ ...role, isTemplate: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {XoAclRole['id']} id
|
||||
* @returns {Promise<Omit<XoAclRole, 'privilegeIds' | 'groupIds' | 'userIds'>>}
|
||||
*/
|
||||
async #getRawAclV2Role(id) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.#roleDb.first(id)
|
||||
if (role === undefined) {
|
||||
throw noSuchObject(id, 'role')
|
||||
}
|
||||
|
||||
return role
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} role
|
||||
* @param {XoAclRole['name']} role.name
|
||||
@@ -257,7 +272,7 @@ export default class {
|
||||
async deleteAclV2Role(id, { force = false } = {}) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.getAclV2Role(id)
|
||||
const role = await this.#getRawAclV2Role(id)
|
||||
|
||||
if (!force && 'isTemplate' in role) {
|
||||
throw forbiddenOperation('delete ACL V2 role', 'role is a template')
|
||||
@@ -290,7 +305,7 @@ export default class {
|
||||
async updateAclV2Role(id, { name, description }, { force = false } = {}) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.getAclV2Role(id)
|
||||
const role = await this.#getRawAclV2Role(id)
|
||||
if (!force && 'isTemplate' in role) {
|
||||
throw forbiddenOperation('update ACL V2 role', 'role is a template')
|
||||
}
|
||||
@@ -309,6 +324,28 @@ export default class {
|
||||
return this.#roleDb.update(role)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {XoAclRole} role
|
||||
*/
|
||||
async #normalizeAclV2Role(role) {
|
||||
if (!('isTemplate' in role)) {
|
||||
/** @type {[UserRole[], GroupRole[]]} */
|
||||
const [userRoles, groupRoles] = await Promise.all([
|
||||
this.#userRoleDb._get({ roleId: role.id }),
|
||||
this.#groupRoleDb._get({ roleId: role.id }),
|
||||
])
|
||||
|
||||
role.userIds = userRoles.map(userRole => userRole.userId)
|
||||
role.groupIds = groupRoles.map(groupRole => groupRole.groupId)
|
||||
}
|
||||
|
||||
/** @type {Privilege[]} */
|
||||
const privileges = await this.#privilegeDb._get({ roleId: role.id })
|
||||
role.privilegeIds = privileges.map(privilege => privilege.id)
|
||||
|
||||
return role
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {XoAclRole['id']} id
|
||||
* @returns {Promise<XoAclRole>}
|
||||
@@ -321,7 +358,7 @@ export default class {
|
||||
throw noSuchObject(id, 'role')
|
||||
}
|
||||
|
||||
return role
|
||||
return this.#normalizeAclV2Role(role)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,9 +366,9 @@ export default class {
|
||||
*/
|
||||
async getAclV2Roles() {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const roles = await this.#roleDb.get()
|
||||
// @ts-ignore typed as Promise<void>...
|
||||
return this.#roleDb.get()
|
||||
return Promise.all(roles.map(role => this.#normalizeAclV2Role(role)))
|
||||
}
|
||||
// === Role
|
||||
// === Privilege
|
||||
@@ -350,7 +387,7 @@ export default class {
|
||||
async createAclV2Privilege({ action, selector, effect = 'allow', resource, roleId }, { force = false } = {}) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.getAclV2Role(roleId)
|
||||
const role = await this.#getRawAclV2Role(roleId)
|
||||
if (!force && 'isTemplate' in role) {
|
||||
throw forbiddenOperation('create ACL V2 privilege', 'role is a template')
|
||||
}
|
||||
@@ -369,7 +406,7 @@ export default class {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const privilege = await this.getAclV2Privilege(id)
|
||||
const role = await this.getAclV2Role(privilege.roleId)
|
||||
const role = await this.#getRawAclV2Role(privilege.roleId)
|
||||
|
||||
if (!force && 'isTemplate' in role) {
|
||||
throw forbiddenOperation('delete ACL V2 privilege', 'role is a template')
|
||||
@@ -392,7 +429,7 @@ export default class {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const privilege = await this.getAclV2Privilege(id)
|
||||
const role = await this.getAclV2Role(privilege.roleId)
|
||||
const role = await this.#getRawAclV2Role(privilege.roleId)
|
||||
|
||||
if ('isTemplate' in role) {
|
||||
throw forbiddenOperation('update ACL V2 privilege', 'role is a template')
|
||||
@@ -472,7 +509,7 @@ export default class {
|
||||
throw objectAlreadyExists({ objectId: userRole.id, objectType: 'userRole' })
|
||||
}
|
||||
|
||||
const role = await this.getAclV2Role(roleId)
|
||||
const role = await this.#getRawAclV2Role(roleId)
|
||||
if ('isTemplate' in role) {
|
||||
throw forbiddenOperation('attach ACL V2 role to user', 'role is a template')
|
||||
}
|
||||
@@ -532,7 +569,7 @@ export default class {
|
||||
throw objectAlreadyExists({ objectId: groupRole.id, objectType: 'groupRole' })
|
||||
}
|
||||
|
||||
const role = await this.getAclV2Role(roleId)
|
||||
const role = await this.#getRawAclV2Role(roleId)
|
||||
if ('isTemplate' in role) {
|
||||
throw forbiddenOperation('attach ACL V2 role to group', 'role is a template')
|
||||
}
|
||||
@@ -574,7 +611,7 @@ export default class {
|
||||
async getAclV2RolePrivileges(roleId) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.getAclV2Role(roleId)
|
||||
const role = await this.#getRawAclV2Role(roleId)
|
||||
return this.#privilegeDb._get({ roleId: role.id })
|
||||
}
|
||||
|
||||
@@ -631,7 +668,7 @@ export default class {
|
||||
async copyAclV2Role(roleId, params = {}) {
|
||||
await this._app.checkFeatureAuthorization('RBAC')
|
||||
|
||||
const role = await this.getAclV2Role(roleId)
|
||||
const role = await this.#getRawAclV2Role(roleId)
|
||||
const privileges = await this.getAclV2RolePrivileges(roleId)
|
||||
|
||||
const replicaRole = await this.createAclV2Role({
|
||||
|
||||
Reference in New Issue
Block a user