1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-29 18:50:08 +03:00

OpenNebula/one#6124: ACL tab (#2879)

This commit is contained in:
David 2023-12-22 13:50:53 +01:00 committed by GitHub
parent 547d1c4fdd
commit 34e3698d5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 4549 additions and 203 deletions

View File

@ -42,6 +42,7 @@ import {
Shuffle as VRoutersIcons,
ModernTv as VmsIcons,
MinusPinAlt as ZoneIcon,
KeyAlt as ACLIcon,
} from 'iconoir-react'
import loadable from '@loadable/component'
@ -261,7 +262,10 @@ const BackupJobsCreate = loadable(
}
)
// const ACLs = loadable(() => import('client/containers/ACLs'), { ssr: false })
const ACLs = loadable(() => import('client/containers/ACLs'), { ssr: false })
const CreateACLs = loadable(() => import('client/containers/ACLs/Create'), {
ssr: false,
})
export const PATH = {
INSTANCE: {
@ -381,6 +385,10 @@ export const PATH = {
DETAIL: `/${RESOURCE_NAMES.VDC}/:id`,
CREATE: `/${RESOURCE_NAMES.VDC}/create`,
},
ACLS: {
LIST: `/${RESOURCE_NAMES.ACL}`,
CREATE: `/${RESOURCE_NAMES.ACL}/create`,
},
},
}
@ -771,6 +779,18 @@ const ENDPOINTS = [
path: PATH.SYSTEM.VDCS.DETAIL,
Component: VDCDetail,
},
{
title: T.ACLs,
path: PATH.SYSTEM.ACLS.CREATE,
Component: CreateACLs,
},
{
title: T.ACLs,
path: PATH.SYSTEM.ACLS.LIST,
sidebar: true,
icon: ACLIcon,
Component: ACLs,
},
],
},
]

View File

@ -0,0 +1,130 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import { Component } from 'react'
import { ACL_USERS } from 'client/constants'
import { rowStyles } from 'client/components/Tables/styles'
/**
* ACLCardIcon component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardIcon = ({ acl, rootProps }) => {
const {
ID,
idUserId,
idUserType,
resources,
idResourceId,
idResourceType,
rights,
zoneId,
zoneType,
} = acl
// Row styles
const classes = rowStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div
className={classes.title}
style={{ display: 'flex', width: '100%' }}
>
<div style={{ flex: '10%' }} data-cy="acl-card-user">
<span>
{ACL_USERS[idUserType].id}
{idUserId}
</span>
</div>
<div style={{ flex: '25%' }} data-cy="acl-card-resources">
{resources.includes('VM') ? 'V' : '-'}
{resources.includes('HOST') ? 'H' : '-'}
{resources.includes('NET') ? 'N' : '-'}
{resources.includes('IMAGE') ? 'I' : '-'}
{resources.includes('USER') ? 'U' : '-'}
{resources.includes('TEMPLATE') ? 'T' : '-'}
{resources.includes('GROUP') ? 'G' : '-'}
{resources.includes('DATASTORE') ? 'D' : '-'}
{resources.includes('CLUSTER') ? 'C' : '-'}
{resources.includes('DOCUMENT') ? 'O' : '-'}
{resources.includes('ZONE') ? 'Z' : '-'}
{resources.includes('SECGROUP') ? 'S' : '-'}
{resources.includes('VDC') ? 'v' : '-'}
{resources.includes('VROUTER') ? 'R' : '-'}
{resources.includes('MARKETPLACE') ? 'M' : '-'}
{resources.includes('MARKETPLACEAPP') ? 'A' : '-'}
{resources.includes('VMGROUP') ? 'P' : '-'}
{resources.includes('VNTEMPLATE') ? 't' : '-'}
{resources.includes('BACKUPJOB') ? 'B' : '-'}
</div>
<div style={{ flex: '10%' }} data-cy="acl-card-resourcesIdentifier">
<span>
{ACL_USERS[idResourceType].id}
{idResourceId}
</span>
</div>
<div style={{ flex: '10%' }} data-cy="acl-card-rights">
{rights.includes('USE') ? 'u' : '-'}
{rights.includes('MANAGE') ? 'm' : '-'}
{rights.includes('ADMIN') ? 'a' : '-'}
{rights.includes('CREATE') ? 'c' : '-'}
</div>
<div style={{ flex: '10%' }} data-cy="acl-card-zone">
<span>
{ACL_USERS[zoneType].id}
{zoneId}
</span>
</div>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
</div>
</div>
</div>
)
}
ACLCardIcon.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
idUserId: PropTypes.string.isRequired,
idUserName: PropTypes.string,
idUserType: PropTypes.string.isRequired,
resources: PropTypes.array.isRequired,
idResourceId: PropTypes.string,
idResourceName: PropTypes.string,
idResourceType: PropTypes.string.isRequired,
rights: PropTypes.array.isRequired,
zoneId: PropTypes.string,
zoneName: PropTypes.string,
zoneType: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardIcon.displayName = 'ACLCardIcon'
export default ACLCardIcon

View File

@ -0,0 +1,447 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import {
Server as ClusterIcon,
Db as DatastoreIcon,
Folder as VmGroupIcon,
Group as GroupIcon,
HardDrive as HostIcon,
BoxIso as ImageIcon,
CloudDownload as MarketplaceAppIcon,
SimpleCart as MarketplaceIcon,
NetworkAlt as NetworkIcon,
HistoricShield as SecurityGroupIcon,
Packages as ServicesIcon,
EmptyPage as TemplateIcon,
User as UserIcon,
List as VDCIcon,
Shuffle as VRoutersIcons,
ModernTv as VmsIcons,
MinusPinAlt as ZoneIcon,
Globe as AllIcon,
ClockOutline as BackupJobsIcon,
} from 'iconoir-react'
import PropTypes from 'prop-types'
import { Component } from 'react'
import { Typography, Tooltip } from '@mui/material'
import { ACL_USERS, T } from 'client/constants'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { aclStyles } from 'client/components/Cards/ACLCards/styles'
/**
* ACLCardIcon component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardIcon = ({ acl, rootProps }) => {
const {
ID,
idUserName,
idUserType,
resources,
idResourceId,
idResourceName,
idResourceType,
rights,
zoneName,
zoneType,
} = acl
// Row styles
const classes = rowStyles()
// ACL card styles
const aclClasses = aclStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div className={`${classes.title}`}>
<Typography noWrap component="span" data-cy="acl-card-icons">
<Tooltip title={Tr(T.VMs)}>
<VmsIcons
data-cy="acl-card-icon-VM"
className={
resources.includes('VM')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Hosts)}>
<HostIcon
data-cy="acl-card-icon-HOST"
className={
resources.includes('HOST')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Networks)}>
<NetworkIcon
data-cy="acl-card-icon-NET"
className={
resources.includes('NET')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Images)}>
<ImageIcon
data-cy="acl-card-icon-IMAGE"
className={
resources.includes('IMAGE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Users)}>
<UserIcon
data-cy="acl-card-icon-USER"
className={
resources.includes('USER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Templates)}>
<TemplateIcon
data-cy="acl-card-icon-TEMPLATE"
className={
resources.includes('TEMPLATE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Groups)}>
<GroupIcon
data-cy="acl-card-icon-GROUP"
className={
resources.includes('GROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Datastores)}>
<DatastoreIcon
data-cy="acl-card-icon-DATASTORE"
className={
resources.includes('DATASTORE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Clusters)}>
<ClusterIcon
data-cy="acl-card-icon-CLUSTER"
className={
resources.includes('CLUSTER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Services)}>
<ServicesIcon
data-cy="acl-card-icon-DOCUMENT"
className={
resources.includes('DOCUMENT')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Zones)}>
<ZoneIcon
data-cy="acl-card-icon-ZONE"
className={
resources.includes('ZONE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.SecurityGroups)}>
<SecurityGroupIcon
data-cy="acl-card-icon-SECGROUP"
className={
resources.includes('SECGROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.VDCs)}>
<VDCIcon
data-cy="acl-card-icon-VDC"
className={
resources.includes('VDC')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.VirtualRouters)}>
<VRoutersIcons
data-cy="acl-card-icon-VROUTER"
className={
resources.includes('VROUTER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Marketplaces)}>
<MarketplaceAppIcon
data-cy="acl-card-icon-MARKETPLACE"
className={
resources.includes('MARKETPLACE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.Apps)}>
<MarketplaceIcon
data-cy="acl-card-icon-MARKETPLACEAPP"
className={
resources.includes('MARKETPLACEAPP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.VMGroups)}>
<VmGroupIcon
data-cy="acl-card-icon-VMGROUP"
className={
resources.includes('VMGROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.NetworkTemplates)}>
<TemplateIcon
data-cy="acl-card-icon-VNTEMPLATE"
className={
resources.includes('VNTEMPLATE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
<Tooltip title={Tr(T.BackupJob)}>
<BackupJobsIcon
data-cy="acl-card-icon-BACKUPJOB"
className={
resources.includes('BACKUPJOB')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
/>
</Tooltip>
</Typography>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
{idResourceType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.individual.tooltip'],
idResourceId,
])}
>
<span>
<span data-cy="acl-card-resourcesIdentifier">
{Tr(T.Identifier)} #{idResourceId}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.group.tooltip'],
idResourceName,
])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.CLUSTER.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.cluster.tooltip'],
idResourceName,
])}
>
<span>
<ClusterIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.resources.all.tooltip']])}>
<span>
<AllIcon />
<span data-cy="acl-card-resourcesIdentifier">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
</div>
<div className={aclClasses.contentWrapper}>
<div className={classes.caption}>
{idUserType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.user.tooltip'], idUserName])}
>
<span>
<UserIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.group.tooltip'], idUserName])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.all.tooltip']])}>
<span>
<AllIcon />
<span data-cy="acl-card-user">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption}>
{zoneType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.zone.tooltip'], zoneName])}
>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{zoneName}</span>
</span>
</Tooltip>
)}
{zoneType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.zone.tooltip.all']])}>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption} data-cy="acl-card-rights">
<span
data-cy="acl-card-rights-USE"
className={
rights.includes('USE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Use)}
</span>
<span
data-cy="acl-card-rights-MANAGE"
className={
rights.includes('MANAGE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Manage)}
</span>
<span
data-cy="acl-card-rights-ADMIN"
className={
rights.includes('ADMIN')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Admin)}
</span>
<span
data-cy="acl-card-rights-CREATE"
className={
rights.includes('CREATE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Create)}
</span>
</div>
</div>
</div>
)
}
ACLCardIcon.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
idUserName: PropTypes.string,
idUserType: PropTypes.string.isRequired,
resources: PropTypes.array.isRequired,
idResourceId: PropTypes.string,
idResourceName: PropTypes.string,
idResourceType: PropTypes.string.isRequired,
rights: PropTypes.array.isRequired,
zoneName: PropTypes.string,
zoneType: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardIcon.displayName = 'ACLCardIcon'
export default ACLCardIcon

View File

@ -0,0 +1,432 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import {
Server as ClusterIcon,
Group as GroupIcon,
User as UserIcon,
MinusPinAlt as ZoneIcon,
Globe as AllIcon,
} from 'iconoir-react'
import PropTypes from 'prop-types'
import { Component } from 'react'
import { Tooltip } from '@mui/material'
import { ACL_USERS, T } from 'client/constants'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { aclStyles } from 'client/components/Cards/ACLCards/styles'
/**
* ACLCardNames component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardNames = ({ acl, rootProps }) => {
const {
ID,
idUserName,
idUserType,
resources,
idResourceId,
idResourceName,
idResourceType,
rights,
zoneName,
zoneType,
} = acl
// Row styles
const classes = rowStyles()
// ACL card styles
const aclClasses = aclStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div className={`${classes.caption}`} data-cy="acl-card-names">
<span
data-cy="acl-card-name-VM"
className={
resources.includes('VM')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.VMs)}
</span>
<span
data-cy="acl-card-name-HOST"
className={
resources.includes('HOST')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Hosts)}
</span>
<span
data-cy="acl-card-name-NET"
className={
resources.includes('NET')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Networks)}
</span>
<span
data-cy="acl-card-name-IMAGE"
className={
resources.includes('IMAGE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Images)}
</span>
<span
data-cy="acl-card-name-USER"
className={
resources.includes('USER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Users)}
</span>
<span
data-cy="acl-card-name-TEMPLATE"
className={
resources.includes('TEMPLATE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Templates)}
</span>
<span
data-cy="acl-card-name-GROUP"
className={
resources.includes('GROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Groups)}
</span>
<span
data-cy="acl-card-name-DATASTORE"
className={
resources.includes('DATASTORE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Datastores)}
</span>
<span
data-cy="acl-card-name-CLUSTER"
className={
resources.includes('CLUSTER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Clusters)}
</span>
<span
data-cy="acl-card-name-DOCUMENT"
className={
resources.includes('DOCUMENT')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Services)}
</span>
<span
data-cy="acl-card-name-ZONE"
className={
resources.includes('ZONE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Zones)}
</span>
<span
data-cy="acl-card-name-SECGROUP"
className={
resources.includes('SECGROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.SecurityGroups)}
</span>
<span
data-cy="acl-card-name-VDC"
className={
resources.includes('VDC')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.VDCs)}
</span>
<span
data-cy="acl-card-name-VROUTER"
className={
resources.includes('VROUTER')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.VirtualRouters)}
</span>
<span
data-cy="acl-card-name-MARKETPLACE"
className={
resources.includes('MARKETPLACE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Apps)}
</span>
<span
data-cy="acl-card-name-MARKETPLACEAPP"
className={
resources.includes('MARKETPLACEAPP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.Marketplaces)}
</span>
<span
data-cy="acl-card-name-VMGROUP"
className={
resources.includes('VMGROUP')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.VMGroups)}
</span>
<span
data-cy="acl-card-name-VNTEMPLATE"
className={
resources.includes('VNTEMPLATE')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.NetworkTemplates)}
</span>
<span
data-cy="acl-card-name-BACKUPJOB"
className={
resources.includes('BACKUPJOB')
? aclClasses.aclApplies
: aclClasses.aclNotApplies
}
>
{Tr(T.BackupJob)}
</span>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
{idResourceType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.individual.tooltip'],
idResourceId,
])}
>
<span>
<span data-cy="acl-card-resourcesIdentifier">
{Tr(T.Identifier)} #{idResourceId}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.group.tooltip'],
idResourceName,
])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.CLUSTER.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.cluster.tooltip'],
idResourceName,
])}
>
<span>
<ClusterIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.resources.all.tooltip']])}>
<span>
<AllIcon />
<span>{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
</div>
<div className={aclClasses.contentWrapper}>
<div className={classes.caption}>
{idUserType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.user.tooltip'], idUserName])}
>
<span>
<UserIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.group.tooltip'], idUserName])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.all.tooltip']])}>
<span>
<AllIcon />
<span data-cy="acl-card-user">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption}>
{zoneType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.zone.tooltip'], zoneName])}
>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{zoneName}</span>
</span>
</Tooltip>
)}
{zoneType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.zone.tooltip.all']])}>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption} data-cy="acl-card-rights">
<span
data-cy="acl-card-rights-USE"
className={
rights.includes('USE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Use)}
</span>
<span
data-cy="acl-card-rights-MANAGE"
className={
rights.includes('MANAGE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Manage)}
</span>
<span
data-cy="acl-card-rights-ADMIN"
className={
rights.includes('ADMIN')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Admin)}
</span>
<span
data-cy="acl-card-rights-CREATE"
className={
rights.includes('CREATE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Create)}
</span>
</div>
</div>
</div>
)
}
ACLCardNames.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
idUserName: PropTypes.string,
idUserType: PropTypes.string.isRequired,
resources: PropTypes.array.isRequired,
idResourceId: PropTypes.string,
idResourceName: PropTypes.string,
idResourceType: PropTypes.string.isRequired,
rights: PropTypes.array.isRequired,
zoneName: PropTypes.string,
zoneType: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardNames.displayName = 'ACLCardNames'
export default ACLCardNames

View File

@ -0,0 +1,63 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import { Component } from 'react'
import { rowStyles } from 'client/components/Tables/styles'
import { translateACL } from 'client/models/ACL'
/**
* ACLCardReadableRule component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardReadableRule = ({ acl, rootProps }) => {
const { ID, STRING } = acl
// Row styles
const classes = rowStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div className={classes.title}>
<span data-cy="acl-card-readable">{translateACL(STRING)}</span>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
</div>
</div>
</div>
)
}
ACLCardReadableRule.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
STRING: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardReadableRule.displayName = 'ACLCardCLI'
export default ACLCardReadableRule

View File

@ -0,0 +1,410 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import {
Server as ClusterIcon,
Db as DatastoreIcon,
Folder as VmGroupIcon,
Group as GroupIcon,
HardDrive as HostIcon,
BoxIso as ImageIcon,
CloudDownload as MarketplaceAppIcon,
SimpleCart as MarketplaceIcon,
NetworkAlt as NetworkIcon,
HistoricShield as SecurityGroupIcon,
Packages as ServicesIcon,
EmptyPage as TemplateIcon,
User as UserIcon,
List as VDCIcon,
Shuffle as VRoutersIcons,
ModernTv as VmsIcons,
MinusPinAlt as ZoneIcon,
Globe as AllIcon,
ClockOutline as BackupJobsIcon,
} from 'iconoir-react'
import PropTypes from 'prop-types'
import { Component } from 'react'
import { Typography, Tooltip } from '@mui/material'
import { ACL_USERS, T } from 'client/constants'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { aclStyles } from 'client/components/Cards/ACLCards/styles'
/**
* ACLCardIcon component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardIcon = ({ acl, rootProps }) => {
const {
ID,
idUserName,
idUserType,
resources,
idResourceId,
idResourceName,
idResourceType,
rights,
zoneName,
zoneType,
} = acl
// Row styles
const classes = rowStyles()
// ACL card styles
const aclClasses = aclStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div className={`${classes.title}`}>
<Typography noWrap component="span" data-cy="acl-card-icons">
{resources.includes('VM') && (
<Tooltip title={Tr(T.VMs)}>
<VmsIcons
data-cy="acl-card-icon-VM"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('HOST') && (
<Tooltip title={Tr(T.Hosts)}>
<HostIcon
data-cy="acl-card-icon-HOST"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('NET') && (
<Tooltip title={Tr(T.Networks)}>
<NetworkIcon
data-cy="acl-card-icon-NET"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('IMAGE') && (
<Tooltip title={Tr(T.Images)}>
<ImageIcon
data-cy="acl-card-icon-IMAGE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('USER') && (
<Tooltip title={Tr(T.Users)}>
<UserIcon
data-cy="acl-card-icon-USER"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('TEMPLATE') && (
<Tooltip title={Tr(T.Templates)}>
<TemplateIcon
data-cy="acl-card-icon-TEMPLATE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('GROUP') && (
<Tooltip title={Tr(T.Groups)}>
<GroupIcon
data-cy="acl-card-icon-GROUP"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('DATASTORE') && (
<Tooltip title={Tr(T.Datastores)}>
<DatastoreIcon
data-cy="acl-card-icon-DATASTORE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('CLUSTER') && (
<Tooltip title={Tr(T.Clusters)}>
<ClusterIcon
data-cy="acl-card-icon-CLUSTER"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('DOCUMENT') && (
<Tooltip title={Tr(T.Services)}>
<ServicesIcon
data-cy="acl-card-icon-DOCUMENT"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('ZONE') && (
<Tooltip title={Tr(T.Zones)}>
<ZoneIcon
data-cy="acl-card-icon-ZONE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('SECGROUP') && (
<Tooltip title={Tr(T.SecurityGroups)}>
<SecurityGroupIcon
data-cy="acl-card-icon-SECGROUP"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('VDC') && (
<Tooltip title={Tr(T.VDCs)}>
<VDCIcon
data-cy="acl-card-icon-VDC"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('VROUTER') && (
<Tooltip title={Tr(T.VirtualRouters)}>
<VRoutersIcons
data-cy="acl-card-icon-VROUTER"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('MARKETPLACE') && (
<Tooltip title={Tr(T.Marketplaces)}>
<MarketplaceAppIcon
data-cy="acl-card-icon-MARKETPLACE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('MARKETPLACEAPP') && (
<Tooltip title={Tr(T.Apps)}>
<MarketplaceIcon
data-cy="acl-card-icon-MARKETPLACEAPP"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('VMGROUP') && (
<Tooltip title={Tr(T.VMGroups)}>
<VmGroupIcon
data-cy="acl-card-icon-VMGROUP"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('VNTEMPLATE') && (
<Tooltip title={Tr(T.NetworkTemplates)}>
<TemplateIcon
data-cy="acl-card-icon-VNTEMPLATE"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
{resources.includes('BACKUPJOB') && (
<Tooltip title={Tr(T.BackupJob)}>
<BackupJobsIcon
data-cy="acl-card-icon-BACKUPJOB"
className={aclClasses.aclApplies}
/>
</Tooltip>
)}
</Typography>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
{idResourceType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.individual.tooltip'],
idResourceId,
])}
>
<span>
<span data-cy="acl-card-resourcesIdentifier">
{Tr(T.Identifier)} #{idResourceId}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.group.tooltip'],
idResourceName,
])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.CLUSTER.type && (
<Tooltip
title={Tr([
T['acls.table.card.resources.cluster.tooltip'],
idResourceName,
])}
>
<span>
<ClusterIcon />
<span data-cy="acl-card-resourcesIdentifier">
{idResourceName}
</span>
</span>
</Tooltip>
)}
{idResourceType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.resources.all.tooltip']])}>
<span>
<AllIcon />
<span data-cy="acl-card-resourcesIdentifier">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
</div>
<div className={aclClasses.contentWrapper}>
<div className={classes.caption}>
{idUserType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.user.tooltip'], idUserName])}
>
<span>
<UserIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.GROUP.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.group.tooltip'], idUserName])}
>
<span>
<GroupIcon />
<span data-cy="acl-card-user">{idUserName}</span>
</span>
</Tooltip>
)}
{idUserType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.all.tooltip']])}>
<span>
<AllIcon />
<span data-cy="acl-card-user">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption}>
{zoneType === ACL_USERS.INDIVIDUAL.type && (
<Tooltip
title={Tr([T['acls.table.card.rule.zone.tooltip'], zoneName])}
>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{zoneName}</span>
</span>
</Tooltip>
)}
{zoneType === ACL_USERS.ALL.type && (
<Tooltip title={Tr([T['acls.table.card.rule.zone.tooltip.all']])}>
<span>
<ZoneIcon />
<span data-cy="acl-card-zone">{Tr(T.All)}</span>
</span>
</Tooltip>
)}
</div>
<div className={classes.caption} data-cy="acl-card-rights">
<span
data-cy="acl-card-rights-USE"
className={
rights.includes('USE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Use)}
</span>
<span
data-cy="acl-card-rights-MANAGE"
className={
rights.includes('MANAGE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Manage)}
</span>
<span
data-cy="acl-card-rights-ADMIN"
className={
rights.includes('ADMIN')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Admin)}
</span>
<span
data-cy="acl-card-rights-CREATE"
className={
rights.includes('CREATE')
? aclClasses.rigthApplies
: aclClasses.rigthNotApplies
}
>
{Tr(T.Create)}
</span>
</div>
</div>
</div>
)
}
ACLCardIcon.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
idUserName: PropTypes.string,
idUserType: PropTypes.string.isRequired,
resources: PropTypes.array.isRequired,
idResourceId: PropTypes.string,
idResourceName: PropTypes.string,
idResourceType: PropTypes.string.isRequired,
rights: PropTypes.array.isRequired,
zoneName: PropTypes.string,
zoneType: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardIcon.displayName = 'ACLCardIcon'
export default ACLCardIcon

View File

@ -0,0 +1,61 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import { Component } from 'react'
import { rowStyles } from 'client/components/Tables/styles'
/**
* ACLCardCLI component to display ACL details.
*
* @param {object} props - Component props
* @param {object} props.acl - ACL details
* @param {object} props.rootProps - Additional props for the root element
* @returns {Component} UserCard component
*/
const ACLCardCLI = ({ acl, rootProps }) => {
const { ID, STRING } = acl
// Row styles
const classes = rowStyles()
return (
<div {...rootProps} data-cy={`acl-${ID}`}>
<div className={classes.main}>
<div className={classes.title}>
<span data-cy="acl-card-string">{`${STRING}`}</span>
</div>
<div className={classes.caption}>
<span data-cy="acl-card-id">{`#${ID}`}</span>
</div>
</div>
</div>
)
}
ACLCardCLI.propTypes = {
acl: PropTypes.shape({
ID: PropTypes.string.isRequired,
STRING: PropTypes.string.isRequired,
}).isRequired,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
}
ACLCardCLI.displayName = 'ACLCardCLI'
export default ACLCardCLI

View File

@ -0,0 +1,48 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import makeStyles from '@mui/styles/makeStyles'
import { SCHEMES } from 'client/constants'
export const aclStyles = makeStyles(({ palette }) => ({
aclApplies: {
margin: '0.2em',
color: palette.mode === SCHEMES.LIGHT ? 'black' : 'white',
},
aclNotApplies: {
margin: '0.2em',
color: 'grey',
},
centeredContent: {
display: 'flex',
alignItems: 'center',
justifyContent: 'left',
},
rightContent: {
display: 'flex',
alignItems: 'center',
justifyContent: 'right',
},
rigthApplies: {
color: palette.mode === SCHEMES.LIGHT ? 'black' : 'white',
},
rigthNotApplies: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'flex-end',
color: 'grey',
},
}))

View File

@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import ACLCardIcons from 'client/components/Cards/ACLCards/ACLCardIcons'
import ACLCardNames from 'client/components/Cards/ACLCards/ACLCardNames'
import ACLCardCLI from 'client/components/Cards/ACLCards/ACLCardCLI'
import ACLCardResources from 'client/components/Cards/ACLCards/ACLCardResources'
import ACLCardRule from 'client/components/Cards/ACLCards/ACLCardRule'
import ACLCardReadableRule from 'client/components/Cards/ACLCards/ACLCardReadableRule'
import AddressRangeCard from 'client/components/Cards/AddressRangeCard'
import ApplicationCard from 'client/components/Cards/ApplicationCard'
import ApplicationNetworkCard from 'client/components/Cards/ApplicationNetworkCard'
@ -49,6 +55,12 @@ import VmTemplateCard from 'client/components/Cards/VmTemplateCard'
import WavesCard from 'client/components/Cards/WavesCard'
export {
ACLCardIcons,
ACLCardNames,
ACLCardCLI,
ACLCardResources,
ACLCardRule,
ACLCardReadableRule,
AddressRangeCard,
ApplicationCard,
ApplicationNetworkCard,

View File

@ -0,0 +1,68 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import { SCHEMA, FIELDS } from './schema'
import { Grid } from '@mui/material'
import HelperACL from 'client/components/Forms/ACLs/CreateForm/Utils/helper'
export const STEP_ID = 'resources'
const Content = (users, groups, clusters, zones, version) => (
<Grid mt={2} container>
<Grid item xs={8}>
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
</Grid>
<Grid item xs={4}>
<HelperACL
title={T['acls.form.create.resources.title']}
text={T['acls.form.create.resources.info']}
users={users}
groups={groups}
clusters={clusters}
zones={zones}
version={version}
/>
</Grid>
</Grid>
)
/**
* Resources ACL configuration.
*
* @param {object} props - Step props
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {object} Resources ACL configuration step
*/
const Resources = ({ users, groups, clusters, zones, version }) => ({
id: STEP_ID,
label: T['acls.form.create.resources.title'],
resolver: SCHEMA,
optionsValidate: { abortEarly: false },
content: () => Content(users, groups, clusters, zones, version),
})
Resources.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default Resources

View File

@ -0,0 +1,196 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T } from 'client/constants'
import { getObjectSchemaFromFields } from 'client/utils'
import { boolean } from 'yup'
const VM = {
name: 'VM',
label: T['acls.form.create.resources.vm'],
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const DOCUMENT = {
name: 'DOCUMENT',
label: T.Service,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const TEMPLATE = {
name: 'TEMPLATE',
label: T['acls.form.create.resources.vmtemplate'],
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const VMGROUP = {
name: 'VMGROUP',
label: T.VMGroup,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const DATASTORE = {
name: 'DATASTORE',
label: T.Datastore,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const IMAGE = {
name: 'IMAGE',
label: T.Image,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const MARKETPLACE = {
name: 'MARKETPLACE',
label: T.Marketplace,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const MARKETPLACEAPP = {
name: 'MARKETPLACEAPP',
label: T.Apps,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const BACKUPJOB = {
name: 'BACKUPJOB',
label: T.BackupJob,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const NET = {
name: 'NET',
label: T.VirtualNetwork,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const VNTEMPLATE = {
name: 'VNTEMPLATE',
label: T['acls.form.create.resources.vnettemplate'],
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const VROUTER = {
name: 'VROUTER',
label: T.VirtualRouter,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const SECURITY_GROUP = {
name: 'SECGROUP',
label: T.SecurityGroup,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const CLUSTER = {
name: 'CLUSTER',
label: T.Cluster,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const HOST = {
name: 'HOST',
label: T.Host,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const USER = {
name: 'USER',
label: T.User,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const GROUP = {
name: 'GROUP',
label: T.Group,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const VDC = {
name: 'VDC',
label: T.VDC,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const ZONE = {
name: 'ZONE',
label: T.Zone,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 4 },
}
const FIELDS = [
VM,
DOCUMENT,
TEMPLATE,
VMGROUP,
DATASTORE,
IMAGE,
MARKETPLACE,
MARKETPLACEAPP,
BACKUPJOB,
NET,
VNTEMPLATE,
VROUTER,
SECURITY_GROUP,
CLUSTER,
HOST,
USER,
GROUP,
VDC,
ZONE,
]
const SCHEMA = getObjectSchemaFromFields(FIELDS)
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,68 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import { SCHEMA, FIELDS } from './schema'
import { Grid } from '@mui/material'
import HelperACL from 'client/components/Forms/ACLs/CreateForm/Utils/helper'
export const STEP_ID = 'resourcesIdentifier'
const Content = (users, groups, clusters, zones, version) => (
<Grid mt={2} container>
<Grid item xs={8}>
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
</Grid>
<Grid item xs={4}>
<HelperACL
title={T['acls.form.create.resourcesUser.title']}
text={T['acls.form.create.resourcesUser.info']}
users={users}
groups={groups}
clusters={clusters}
zones={zones}
version={version}
/>
</Grid>
</Grid>
)
/**
* Resources identifier ACL configuration.
*
* @param {object} props - Step props
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {object} Resources identifier ACL configuration step
*/
const ResourcesIdentifier = ({ users, groups, clusters, zones, version }) => ({
id: STEP_ID,
label: T['acls.form.create.resourcesUser.title'],
resolver: SCHEMA,
optionsValidate: { abortEarly: false },
content: () => Content(users, groups, clusters, zones, version),
})
ResourcesIdentifier.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default ResourcesIdentifier

View File

@ -0,0 +1,113 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T } from 'client/constants'
import { Field, getObjectSchemaFromFields, arrayToOptions } from 'client/utils'
import { string } from 'yup'
import { GroupsTable, ClustersTable } from 'client/components/Tables'
// Types of id definition
export const ACL_TYPE_ID_TRANSLATIONS = {
INDIVIDUAL: {
value: 'INDIVIDUAL',
text: T['acls.form.create.resourcesUser.identifier'],
},
GROUP: { value: 'GROUP', text: T.Group },
ALL: { value: 'ALL', text: T.All },
CLUSTER: { value: 'CLUSTER', text: T.Cluster },
}
/** @type {Field} Type field */
export const TYPE = {
name: 'TYPE',
type: INPUT_TYPES.TOGGLE,
values: () =>
arrayToOptions(Object.keys(ACL_TYPE_ID_TRANSLATIONS), {
addEmpty: false,
getText: (key) => ACL_TYPE_ID_TRANSLATIONS[key].text,
getValue: (key) => ACL_TYPE_ID_TRANSLATIONS[key].value,
}),
validation: string()
.trim()
.required()
.uppercase()
.default(() => undefined),
grid: { md: 12 },
}
const INDIVIDUAL = {
name: 'INDIVIDUAL',
label: T['acls.form.create.resourcesUser.individual'],
type: INPUT_TYPES.TEXT,
dependOf: TYPE.name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ID_TRANSLATIONS.INDIVIDUAL.value) &&
INPUT_TYPES.HIDDEN,
validation: string()
.trim()
.when(TYPE.name, (type, schema) =>
type !== ACL_TYPE_ID_TRANSLATIONS.INDIVIDUAL.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
}
const GROUP = {
name: 'GROUP',
label: T['acls.form.create.resourcesUser.group'],
type: INPUT_TYPES.TABLE,
dependOf: TYPE.name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ID_TRANSLATIONS.GROUP.value) &&
INPUT_TYPES.HIDDEN,
Table: () => GroupsTable,
validation: string()
.trim()
.when(TYPE.name, (type, schema) =>
type !== ACL_TYPE_ID_TRANSLATIONS.GROUP.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
}
const CLUSTER = {
name: 'CLUSTER',
label: T['acls.form.create.resourcesUser.cluster'],
type: INPUT_TYPES.TABLE,
dependOf: TYPE.name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ID_TRANSLATIONS.CLUSTER.value) &&
INPUT_TYPES.HIDDEN,
Table: () => ClustersTable,
validation: string()
.trim()
.when(TYPE.name, (type, schema) =>
type !== ACL_TYPE_ID_TRANSLATIONS.CLUSTER.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
}
const FIELDS = [TYPE, INDIVIDUAL, GROUP, CLUSTER]
const SCHEMA = getObjectSchemaFromFields(FIELDS)
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,68 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import { SCHEMA, FIELDS } from './schema'
import { Grid } from '@mui/material'
import HelperACL from 'client/components/Forms/ACLs/CreateForm/Utils/helper'
export const STEP_ID = 'rights'
const Content = (users, groups, clusters, zones, version) => (
<Grid mt={2} container>
<Grid item xs={8}>
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
</Grid>
<Grid item xs={4}>
<HelperACL
title={T['acls.form.create.rights.title']}
text={T['acls.form.create.rights.info']}
users={users}
groups={groups}
clusters={clusters}
zones={zones}
version={version}
/>
</Grid>
</Grid>
)
/**
* Rights ACL configuration.
*
* @param {object} props - Step props
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {object} Rights ACL configuration step
*/
const Rights = ({ users, groups, clusters, zones, version }) => ({
id: STEP_ID,
label: T['acls.form.create.rights.title'],
resolver: SCHEMA,
optionsValidate: { abortEarly: false },
content: () => Content(users, groups, clusters, zones, version),
})
Rights.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default Rights

View File

@ -0,0 +1,56 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T } from 'client/constants'
import { getObjectSchemaFromFields } from 'client/utils'
import { boolean } from 'yup'
const USE = {
name: 'USE',
label: T.Use,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 3 },
}
const MANAGE = {
name: 'MANAGE',
label: T.Manage,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 3 },
}
const ADMIN = {
name: 'ADMIN',
label: T.Admin,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 3 },
}
const CREATE = {
name: 'CREATE',
label: T.Create,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 3 },
}
const FIELDS = [USE, MANAGE, ADMIN, CREATE]
const SCHEMA = getObjectSchemaFromFields(FIELDS)
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,155 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import { SCHEMA, FIELDS } from './schema'
import { Stack, Alert, Typography } from '@mui/material'
import makeStyles from '@mui/styles/makeStyles'
import { Tr } from 'client/components/HOC'
import { useEffect, useState } from 'react'
import { useWatch } from 'react-hook-form'
import { translateACL, validACL } from 'client/models/ACL'
import { generateDocLink } from 'client/utils'
export const STEP_ID = 'stringEditor'
const Content = (version, users, groups, clusters, zones) => {
const [ruleString, setRuleString] = useState('')
const watch = useWatch({
name: 'stringEditor.RULE',
})
useEffect(() => {
setRuleString(watch)
}, [watch])
// Style for info message
const useStyles = makeStyles(({ palette }) => ({
groupInfo: {
'&': {
gridColumn: 'span 2',
marginTop: '1em',
backgroundColor: palette.background.paper,
},
},
}))
const classes = useStyles()
return (
<Stack
display="grid"
gap="1em"
sx={{
gridTemplateColumns: { sm: '1fr', md: '1frs' },
padding: '0.5 em',
}}
>
<Alert severity="info" variant="outlined" className={classes.groupInfo}>
{Tr(T['acls.form.create.stringEditor.info'])}
<ul>
<li>
<b>{Tr(T['acls.form.create.stringEditor.info.user.title'])}</b>
{Tr(T['acls.form.create.stringEditor.info.user.info'])}
</li>
<li>
<b>{Tr(T['acls.form.create.stringEditor.info.resource.title'])}</b>
{Tr(T['acls.form.create.stringEditor.info.resource.info'])}
</li>
<li>
<b>{Tr(T['acls.form.create.stringEditor.info.rights.title'])}</b>
{Tr(T['acls.form.create.stringEditor.info.rights.info'])}
</li>
<li>
<b>{Tr(T['acls.form.create.stringEditor.info.zone.title'])}</b>
{Tr(T['acls.form.create.stringEditor.info.zone.info'])}
</li>
</ul>
{Tr(T['acls.form.create.stringEditor.info.more'])}
<a
target="_blank"
href={generateDocLink(
version,
'management_and_operations/users_groups_management/chmod.html#manage-acl'
)}
rel="noreferrer"
>
{Tr(T['acls.form.create.stringEditor.info.more.link'])}
</a>
</Alert>
<Stack
display="grid"
gap="1em"
sx={{
gridTemplateColumns: { sm: '1fr 1fr', md: '1fr' },
padding: '0.5 em',
}}
>
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
<Typography>
{validACL(ruleString) ? (
<Alert
severity="success"
variant="outlined"
className={classes.groupInfo}
>
{translateACL(ruleString, users, groups, clusters, zones)}
</Alert>
) : (
<Alert
severity="error"
variant="outlined"
className={classes.groupInfo}
>
{Tr(T['acls.translate.error'])}
</Alert>
)}
</Typography>
</Stack>
</Stack>
)
}
/**
* StringEditor ACL configuration.
*
* @param {object} props - Step props
* @param {string} props.version - ONE version
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @returns {object} StringEditor ACL configuration step
*/
const Resources = ({ version, users, groups, clusters, zones }) => ({
id: STEP_ID,
label: T['acls.form.create.stringEditor.title'],
resolver: SCHEMA,
optionsValidate: { abortEarly: false },
content: () => Content(version, users, groups, clusters, zones),
})
Resources.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default Resources

View File

@ -0,0 +1,32 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T } from 'client/constants'
import { getObjectSchemaFromFields } from 'client/utils'
import { string } from 'yup'
const RULE = {
name: 'RULE',
label: T.ACL,
type: INPUT_TYPES.TEXT,
validation: string().default(() => undefined),
grid: { md: 12 },
}
const FIELDS = [RULE]
const SCHEMA = getObjectSchemaFromFields(FIELDS)
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,120 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import { T, ACL_TYPE_ID, ACL_RESOURCES } from 'client/constants'
import { Stack, Alert } from '@mui/material'
import makeStyles from '@mui/styles/makeStyles'
import { Tr } from 'client/components/HOC'
import { useFormContext } from 'react-hook-form'
import { createStringACL, translateACL } from 'client/models/ACL'
import { object } from 'yup'
import { generateDocLink } from 'client/utils'
export const STEP_ID = 'summary'
const Content = (version, users, groups, clusters, zones) => {
const { getValues } = useFormContext()
const values = getValues()
const ruleString = createStringACL(
ACL_TYPE_ID[values?.user?.TYPE],
values?.user?.INDIVIDUAL ?? values?.user?.GROUP ?? values?.user?.CLUSTER,
Object.keys(values?.resources)
.filter((resource) => values?.resources[resource])
.map((resource) => ACL_RESOURCES[resource]),
ACL_TYPE_ID[values?.resourcesIdentifier?.TYPE],
values?.resourcesIdentifier?.INDIVIDUAL ??
values?.resourcesIdentifier?.GROUP ??
values?.resourcesIdentifier?.CLUSTER,
Object.keys(values?.rights).filter((key) => values?.rights[key]),
values?.zone?.TYPE ? ACL_TYPE_ID[values?.zone?.TYPE] : undefined,
values?.zone?.ZONE
)
// Style for info message
const useStyles = makeStyles(({ palette }) => ({
groupInfo: {
'&': {
gridColumn: 'span 2',
marginTop: '1em',
backgroundColor: palette.background.paper,
},
},
}))
const classes = useStyles()
return (
<Stack
display="grid"
gap="1em"
sx={{
gridTemplateColumns: { sm: '1fr', md: '1fr 1fr' },
padding: '0.5 em',
}}
>
<Alert severity="info" variant="outlined" className={classes.groupInfo}>
{Tr(T['acls.form.create.summary.info.rule'])}
<b data-cy="ruleString">{ruleString}</b>
<br />
<br />
{Tr(T['acls.form.create.summary.info.translation'])}
<b>{translateACL(ruleString, users, groups, clusters, zones)}</b>
<br />
<br />
{Tr(T['acls.form.create.stringEditor.info.more'])}
<a
target="_blank"
href={generateDocLink(
version,
'management_and_operations/users_groups_management/chmod.html#manage-acl#manage-acl'
)}
rel="noreferrer"
>
{Tr(T['acls.form.create.stringEditor.info.more.link'])}
</a>
</Alert>
</Stack>
)
}
/**
* Summary ACL configuration.
*
* @param {object} props - Step props
* @param {string} props.version - ONE version
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @returns {object} Summary ACL configuration step
*/
const Summary = ({ version, users, groups, clusters, zones }) => ({
id: STEP_ID,
label: T['acls.form.create.summary.title'],
resolver: object(),
optionsValidate: { abortEarly: false },
content: () => Content(version, users, groups, clusters, zones),
})
Summary.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default Summary

View File

@ -0,0 +1,68 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { SCHEMA, FIELDS } from './schema'
import { Grid } from '@mui/material'
import { T } from 'client/constants'
import HelperACL from 'client/components/Forms/ACLs/CreateForm/Utils/helper'
export const STEP_ID = 'user'
const Content = (users, groups, clusters, zones, version) => (
<Grid mt={2} container>
<Grid item xs={8}>
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
</Grid>
<Grid item xs={4}>
<HelperACL
title={T['acls.form.create.user.title']}
text={T['acls.form.create.user.info']}
users={users}
groups={groups}
clusters={clusters}
zones={zones}
version={version}
/>
</Grid>
</Grid>
)
/**
* User ACL configuration.
*
* @param {object} props - Step props
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {object} User ACL configuration step
*/
const User = ({ users, groups, clusters, zones, version }) => ({
id: STEP_ID,
label: T['acls.form.create.user.title'],
resolver: SCHEMA,
optionsValidate: { abortEarly: false },
content: () => Content(users, groups, clusters, zones, version),
})
User.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default User

View File

@ -0,0 +1,89 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T } from 'client/constants'
import { Field, getObjectSchemaFromFields, arrayToOptions } from 'client/utils'
import { string } from 'yup'
import { UsersTable, GroupsTable } from 'client/components/Tables'
const ACL_TYPE_ID_USER_TRANSLATIONS = {
INDIVIDUAL: { value: 'INDIVIDUAL', text: T.User },
GROUP: { value: 'GROUP', text: T.Group },
ALL: { value: 'ALL', text: T.All },
}
/** @type {Field} Type field */
export const TYPE = {
name: 'TYPE',
type: INPUT_TYPES.TOGGLE,
values: () =>
arrayToOptions(Object.keys(ACL_TYPE_ID_USER_TRANSLATIONS), {
addEmpty: false,
getText: (key) => ACL_TYPE_ID_USER_TRANSLATIONS[key].text,
getValue: (key) => ACL_TYPE_ID_USER_TRANSLATIONS[key].value,
}),
validation: string()
.trim()
.required()
.uppercase()
.default(() => undefined),
grid: { md: 12 },
}
const INDIVIDUAL = {
name: 'INDIVIDUAL',
label: T['acls.form.create.user.individual'],
type: INPUT_TYPES.TABLE,
dependOf: TYPE.name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ID_USER_TRANSLATIONS.INDIVIDUAL.value) &&
INPUT_TYPES.HIDDEN,
Table: () => UsersTable,
validation: string()
.trim()
.when(TYPE.name, (type, schema) =>
type !== ACL_TYPE_ID_USER_TRANSLATIONS.INDIVIDUAL.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
}
const GROUP = {
name: 'GROUP',
label: T['acls.form.create.user.group'],
type: INPUT_TYPES.TABLE,
dependOf: TYPE.name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ID_USER_TRANSLATIONS.GROUP.value) &&
INPUT_TYPES.HIDDEN,
Table: () => GroupsTable,
validation: string()
.trim()
.when(TYPE.name, (type, schema) =>
type !== ACL_TYPE_ID_USER_TRANSLATIONS.GROUP.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
}
const FIELDS = [TYPE, INDIVIDUAL, GROUP]
const SCHEMA = getObjectSchemaFromFields(FIELDS)
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,73 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import { SCHEMA, FIELDS } from './schema'
import { Grid } from '@mui/material'
import HelperACL from 'client/components/Forms/ACLs/CreateForm/Utils/helper'
export const STEP_ID = 'zone'
const Content = (oneConfig, users, groups, clusters, zones, version) => (
<Grid mt={2} container>
<Grid item xs={8}>
<FormWithSchema
id={STEP_ID}
cy={`${STEP_ID}`}
fields={FIELDS(oneConfig)}
/>
</Grid>
<Grid item xs={4}>
<HelperACL
title={T['acls.form.create.zone.title']}
text={T['acls.form.create.zone.info']}
users={users}
groups={groups}
clusters={clusters}
zones={zones}
version={version}
/>
</Grid>
</Grid>
)
/**
* Zone ACL configuration.
*
* @param {object} props - Step props
* @param {object} props.oneConfig - ONE config
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {object} Zone ACL configuration step
*/
const Zone = ({ oneConfig, users, groups, clusters, zones, version }) => ({
id: STEP_ID,
label: T['acls.form.create.zone.title'],
resolver: SCHEMA(oneConfig),
optionsValidate: { abortEarly: false },
content: () => Content(oneConfig, users, groups, clusters, zones, version),
})
Zone.propTypes = {
data: PropTypes.object,
setFormData: PropTypes.func,
}
export default Zone

View File

@ -0,0 +1,87 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { INPUT_TYPES, T, FEDERATION_TYPE } from 'client/constants'
import { Field, getObjectSchemaFromFields, arrayToOptions } from 'client/utils'
import { string } from 'yup'
import { ZonesTable } from 'client/components/Tables'
const ACL_TYPE_ZONE_TRANSLATIONS = {
ALL: { value: 'ALL', text: T.All },
INDIVIDUAL: { value: 'INDIVIDUAL', text: T.Zone },
}
/** @type {Field} Type field */
export const TYPE = (oneConfig) => ({
name: 'TYPE',
type: INPUT_TYPES.TOGGLE,
values: () =>
arrayToOptions(Object.keys(ACL_TYPE_ZONE_TRANSLATIONS), {
addEmpty: false,
getText: (key) => ACL_TYPE_ZONE_TRANSLATIONS[key].text,
getValue: (key) => ACL_TYPE_ZONE_TRANSLATIONS[key].value,
}),
validation:
oneConfig.FEDERATION.MODE === FEDERATION_TYPE.STANDALONE
? string()
: string().required(),
grid: { md: 12 },
})
const ZONE = (oneConfig) => ({
name: 'ZONE',
label: T['acls.form.create.zone.zone'],
type: INPUT_TYPES.TABLE,
dependOf: TYPE(oneConfig).name,
htmlType: (type) =>
(!type || type !== ACL_TYPE_ZONE_TRANSLATIONS.INDIVIDUAL.value) &&
INPUT_TYPES.HIDDEN,
Table: () => ZonesTable,
validation: string()
.trim()
.when(TYPE(oneConfig).name, (type, schema) =>
type !== ACL_TYPE_ZONE_TRANSLATIONS.INDIVIDUAL.value
? schema.strip()
: schema.required()
)
.default(() => undefined),
grid: { md: 12 },
})
/**
* Return all the fields for this schema.
*
* @param {object} oneConfig - . ONE config
* @returns {Array} - The list of fields
*/
const FIELDS = (oneConfig) => {
console.log(oneConfig)
return [TYPE(oneConfig), ZONE(oneConfig)]
}
/**
* Return the schema.
*
* @param {object} oneConfig - . ONE config
* @returns {object} - The schema
*/
const SCHEMA = (oneConfig) => {
console.log(oneConfig)
return getObjectSchemaFromFields(FIELDS(oneConfig))
}
export { SCHEMA, FIELDS }

View File

@ -0,0 +1,103 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import User, {
STEP_ID as USER_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/User'
import Resources, {
STEP_ID as RESOURCES_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/Resources'
import ResourcesIdentifier, {
STEP_ID as RESOURCES_IDENTIFIER_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/ResourcesIdentifier'
import Rights, {
STEP_ID as RIGHTS_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/Rights'
import Zone, {
STEP_ID as ZONE_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/Zone'
import Summary from 'client/components/Forms/ACLs/CreateForm/Steps/Summary'
import StringEditor, {
STEP_ID as STRING_EDITOR_ID,
} from 'client/components/Forms/ACLs/CreateForm/Steps/StringEditor'
import { createSteps } from 'client/utils'
import { ACL_TYPE_ID, ACL_RESOURCES } from 'client/constants'
import { createStringACL } from 'client/models/ACL'
/**
* Create steps for ACL Create Form with wizard:
* 1. User: User or users whom the rule will apply
* 2. Resources: Affected resources by the rule
* 3. ResourcesIdentifier: Identifier of the resources
* 4. Rights: Operations that will be enabled
* 5. Zone: Zone whom the rule will apply
* 6. Summary: Resume of the rules
* Create steps for ACL Create From from string:
* 1. StringEditor: Enter the string rule to create it
*/
const Steps = createSteps(
(stepProps) =>
stepProps?.fromString
? [StringEditor]
: [User, Resources, ResourcesIdentifier, Rights, Zone, Summary],
{
transformBeforeSubmit: (formData) => {
// Get data from steps
const { [USER_ID]: userData } = formData
const { [RESOURCES_ID]: resourcesData } = formData
const { [RESOURCES_IDENTIFIER_ID]: resourcesIdentifierData } = formData
const { [RIGHTS_ID]: rightsData } = formData
const { [ZONE_ID]: zoneData } = formData
const { [STRING_EDITOR_ID]: stringEditorData } = formData
// In case of string editor, we only need the string rule
if (stringEditorData) {
// Return the string rule
return {
string: stringEditorData?.RULE,
}
} else {
// Create the string rule from the data that the user enter on the wizard
const rule = createStringACL(
ACL_TYPE_ID[userData.TYPE],
userData?.INDIVIDUAL ?? userData?.GROUP ?? userData?.CLUSTER,
Object.keys(resourcesData)
.filter((resource) => resourcesData[resource])
.map((resource) => ACL_RESOURCES[resource]),
ACL_TYPE_ID[resourcesIdentifierData?.TYPE],
resourcesIdentifierData?.INDIVIDUAL ??
resourcesIdentifierData?.GROUP ??
resourcesIdentifierData?.CLUSTER,
Object.keys(rightsData).filter((key) => rightsData[key]),
zoneData?.TYPE ? ACL_TYPE_ID[zoneData?.TYPE] : undefined,
zoneData?.ZONE
)
// Return the string rule
return {
string: rule,
}
}
},
}
)
export default Steps

View File

@ -0,0 +1,136 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Card, CardContent, Typography } from '@mui/material'
import { Tr } from 'client/components/HOC'
import { T, ACL_TYPE_ID, ACL_RESOURCES } from 'client/constants'
import { useWatch } from 'react-hook-form'
import { useEffect, useState, Component } from 'react'
import { createStringACL, translateACL } from 'client/models/ACL'
import { generateDocLink } from 'client/utils'
import PropTypes from 'prop-types'
/**
* Card with help texts and the ACL rule in a readable language.
*
* @param {object} props - Component props
* @param {string} props.title - Card title
* @param {string} props.text - Card text
* @param {Array} props.users - List of users
* @param {Array} props.groups - List of groups
* @param {Array} props.clusters - List of clusters
* @param {Array} props.zones - List of zones
* @param {string} props.version - ONE version
* @returns {Component} The HelperACL component.
*/
const HelperACL = ({
title,
text,
users,
groups,
clusters,
zones,
version,
}) => {
// Create rule
const [rule, setRule] = useState('')
// Watch form
const watch = useWatch()
// Create ACL when something changes on the form
useEffect(() => {
const ruleString = createStringACL(
ACL_TYPE_ID[watch?.user?.TYPE],
watch?.user?.INDIVIDUAL ?? watch?.user?.GROUP ?? watch?.user?.CLUSTER,
Object.keys(watch?.resources)
.filter((resource) => watch?.resources[resource])
.map((resource) => ACL_RESOURCES[resource]),
ACL_TYPE_ID[watch?.resourcesIdentifier?.TYPE],
watch?.resourcesIdentifier?.INDIVIDUAL ??
watch?.resourcesIdentifier?.GROUP ??
watch?.resourcesIdentifier?.CLUSTER,
Object.keys(watch?.rights).filter((key) => watch?.rights[key]),
watch?.zone?.TYPE ? ACL_TYPE_ID[watch?.zone?.TYPE] : undefined,
watch?.zone?.ZONE
)
setRule(ruleString)
}, [watch])
return (
<Card
elevation={2}
sx={{
height: '100%',
minHeight: '630px',
maxHeight: '630px',
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
marginLeft: '1em',
marginTop: '1rem',
}}
>
<CardContent
sx={{
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
gap: '1em',
}}
>
<Typography variant="h6" component="div" gutterBottom>
{Tr(title)}
</Typography>
<Typography variant="body2" gutterBottom>
{Tr(text)}
</Typography>
<Typography variant="body2" gutterBottom>
<b>{translateACL(rule, users, groups, clusters, zones)}</b>
</Typography>
<Typography variant="body2" gutterBottom>
{Tr(T['acls.form.create.stringEditor.info.more'])}
<a
target="_blank"
href={generateDocLink(
version,
'management_and_operations/users_groups_management/chmod.html#manage-acl'
)}
rel="noreferrer"
>
{Tr(T['acls.form.create.stringEditor.info.more.link'])}
</a>
</Typography>
</CardContent>
</Card>
)
}
HelperACL.displayName = 'HelperACL'
HelperACL.propTypes = {
title: PropTypes.string,
text: PropTypes.string,
users: PropTypes.array,
groups: PropTypes.array,
clusters: PropTypes.array,
zones: PropTypes.array,
version: PropTypes.string,
}
export default HelperACL

View File

@ -0,0 +1,16 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
export { default } from 'client/components/Forms/ACLs/CreateForm/Steps'

View File

@ -0,0 +1,27 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement } from 'react'
import { AsyncLoadForm, ConfigurationProps } from 'client/components/HOC'
import { CreateStepsCallback } from 'client/utils/schema'
/**
* @param {ConfigurationProps} configProps - Configuration
* @returns {ReactElement|CreateStepsCallback} Asynchronous loaded form
*/
const CreateForm = (configProps) =>
AsyncLoadForm({ formPath: 'ACLs/CreateForm' }, configProps)
export { CreateForm }

View File

@ -0,0 +1,114 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Typography } from '@mui/material'
import { AddCircledOutline, DesignPencil, Trash } from 'iconoir-react'
import { useMemo } from 'react'
import { useHistory } from 'react-router-dom'
import { useViews } from 'client/features/Auth'
import { useRemoveAclMutation } from 'client/features/OneApi/acl'
import {
createActions,
GlobalAction,
} from 'client/components/Tables/Enhanced/Utils'
import { PATH } from 'client/apps/sunstone/routesOne'
import { Translate } from 'client/components/HOC'
import { RESOURCE_NAMES, T, ACL_ACTIONS } from 'client/constants'
const ListACLNames = ({ rows = [] }) =>
rows?.map?.(({ id, original }) => {
const { ID, NAME } = original
return (
<Typography
key={`acl-${id}`}
variant="inherit"
component="span"
display="block"
>
{`#${ID} ${NAME}`}
</Typography>
)
})
const MessageToConfirmAction = (rows, description) => (
<>
<ListACLNames rows={rows} />
{description && <Translate word={description} />}
<Translate word={T.DoYouWantProceed} />
</>
)
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
/**
* Generates the actions to operate resources on ACL table.
*
* @returns {GlobalAction} - Actions
*/
const Actions = () => {
const history = useHistory()
const { view, getResourceView } = useViews()
const [remove] = useRemoveAclMutation()
return useMemo(
() =>
createActions({
filters: getResourceView(RESOURCE_NAMES.ACL)?.actions,
actions: [
{
accessor: ACL_ACTIONS.CREATE_DIALOG,
tooltip: T.Create,
icon: AddCircledOutline,
action: () => history.push(PATH.SYSTEM.ACLS.CREATE, false),
},
{
accessor: ACL_ACTIONS.CREATE_DIALOG_STRING,
tooltip: T['acls.table.actions.create.string'],
icon: DesignPencil,
action: () => history.push(PATH.SYSTEM.ACLS.CREATE, true),
},
{
accessor: ACL_ACTIONS.DELETE,
tooltip: T.Delete,
icon: Trash,
color: 'error',
selected: { min: 1 },
dataCy: `acl_${ACL_ACTIONS.DELETE}`,
options: [
{
isConfirmDialog: true,
dialogProps: {
title: T.Delete,
dataCy: `modal-${ACL_ACTIONS.DELETE}`,
children: MessageToConfirmAction,
},
onSubmit: (rows) => async () => {
const ids = rows?.map?.(({ original }) => original?.ID)
await Promise.all(ids.map((id) => remove({ id })))
},
},
],
},
],
}),
[view]
)
}
export default Actions

View File

@ -0,0 +1,114 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { CategoryFilter } from 'client/components/Tables/Enhanced/Utils'
import { T } from 'client/constants'
const COLUMNS = [
{ Header: T.Identifier, id: 'ID', accessor: 'ID', sortType: 'number' },
{
Header: T['acls.table.filter.string'],
id: 'STRING',
accessor: 'STRING',
sortType: 'number',
},
{
Header: T['acls.table.filter.user.name'],
id: 'idUserName',
accessor: 'USER.name',
sortType: 'number',
},
{
Header: T['acls.table.filter.user.type'],
id: 'idUserType',
accessor: 'USER.type',
sortType: 'number',
},
{
Header: T['acls.table.filter.user.id'],
id: 'idUserId',
accessor: 'USER.id',
sortType: 'number',
},
{
Header: T.Resources,
id: 'resources',
accessor: 'RESOURCE.resources',
sortType: 'string',
disableFilters: false,
Filter: ({ column }) =>
CategoryFilter({
column,
multiple: true,
title: 'Test',
}),
filter: 'arrIncludes',
},
{
Header: T['acls.table.filter.resources.user.name'],
id: 'idResourceName',
accessor: 'RESOURCE.identifier.name',
sortType: 'number',
},
{
Header: T['acls.table.filter.resources.user.type'],
id: 'idResourceType',
accessor: 'RESOURCE.identifier.type',
sortType: 'number',
},
{
Header: T['acls.table.filter.resources.user.id'],
id: 'idResourceId',
accessor: 'RESOURCE.identifier.id',
sortType: 'number',
},
{
Header: T.Rights,
id: 'rights',
accessor: 'RIGHTS.rights',
sortType: 'string',
disableFilters: false,
Filter: ({ column }) =>
CategoryFilter({
column,
multiple: true,
title: 'Test',
}),
filter: 'arrIncludes',
},
{
Header: T['acls.table.filter.zone.name'],
id: 'zoneName',
accessor: 'ZONE.name',
sortType: 'number',
},
{
Header: T['acls.table.filter.zone.type'],
id: 'zoneType',
accessor: 'ZONE.type',
sortType: 'number',
},
{
Header: T['acls.table.filter.zone.id'],
id: 'zoneId',
accessor: 'ZONE.id',
sortType: 'number',
},
]
export default COLUMNS

View File

@ -0,0 +1,94 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, Component, useState } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetAclsExtendedQuery } from 'client/features/OneApi/acl'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import ACLColumns from 'client/components/Tables/ACLs/columns'
import ACLRow from 'client/components/Tables/ACLs/row'
import { RESOURCE_NAMES, ACL_TABLE_VIEWS } from 'client/constants'
const DEFAULT_DATA_CY = 'acls'
/**
* `ACLsTable` component displays a table of ACLs.
*
* @param {object} props - Component properties.
* @param {object} [props.rootProps={}] - Root properties for the table.
* @param {object} [props.searchProps={}] - Search properties for the table.
* @param {Array} props.vdcGroups - Array of VDC groups.
* @param {Array<string|number>} [props.secondaryGroups=[]] - Array of IDs of the secondary groups.
* @param {object} props.rest - Rest of the properties.
* @returns {Component} Rendered component.
*/
const ACLsTable = (props) => {
const {
rootProps = {},
searchProps = {},
vdcGroups,
singleSelect = false,
...rest
} = props ?? {}
rootProps['data-cy'] ??= DEFAULT_DATA_CY
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
const { view, getResourceView } = useViews()
// Get data
const { data = [], isFetching, refetch } = useGetAclsExtendedQuery()
const columns = useMemo(
() =>
createColumns({
filters: getResourceView(RESOURCE_NAMES.ACL)?.filters,
columns: ACLColumns,
}),
[view]
)
// Variable to store the type of table
const [viewType, setViewType] = useState(ACL_TABLE_VIEWS.ICONS.type)
// Define the type of views
const tableViews = {
views: ACL_TABLE_VIEWS,
onClick: (name) => {
setViewType(name)
},
}
return (
data && (
<EnhancedTable
columns={columns}
data={data}
rootProps={rootProps}
searchProps={searchProps}
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={useMemo(() => ACLRow(viewType), [viewType])}
singleSelect={singleSelect}
tableViews={tableViews}
{...rest}
/>
)
)
}
export default ACLsTable

View File

@ -0,0 +1,61 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import {
ACLCardIcons,
ACLCardNames,
ACLCardCLI,
ACLCardResources,
ACLCardRule,
ACLCardReadableRule,
} from 'client/components/Cards'
import { ACL_TABLE_VIEWS } from 'client/constants'
const Row = (viewType) => {
const aclRow = ({ original, value, ...props }) => {
// Check what view show in the table cards
if (viewType === ACL_TABLE_VIEWS.NAMES.type) {
return <ACLCardNames rootProps={props} acl={value} />
} else if (viewType === ACL_TABLE_VIEWS.CLI.type) {
return <ACLCardCLI rootProps={props} acl={value} />
} else if (viewType === ACL_TABLE_VIEWS.RESOURCES.type) {
return <ACLCardResources rootProps={props} acl={value} />
} else if (viewType === ACL_TABLE_VIEWS.RULE.type) {
return <ACLCardRule rootProps={props} acl={value} />
} else if (viewType === ACL_TABLE_VIEWS.READABLERULE.type) {
return <ACLCardReadableRule rootProps={props} acl={value} />
} else {
return <ACLCardIcons rootProps={props} acl={value} />
}
}
aclRow.displayName = 'aclRow'
aclRow.propTypes = {
original: PropTypes.object,
value: PropTypes.object,
}
return aclRow
}
Row.displayName = 'Row'
Row.propTypes = {
viewType: PropTypes.func,
}
export default Row

View File

@ -71,7 +71,12 @@ const CategoryFilter = ({
preFilteredRows?.forEach((row) => {
const rowValue = row.values[id]
rowValue !== undefined && uniqueOptions.add(rowValue)
// If the row value is an array, we get all the values of the array
rowValue !== undefined &&
(Array.isArray(rowValue)
? rowValue.forEach((value) => uniqueOptions.add(value))
: uniqueOptions.add(rowValue))
})
return [...uniqueOptions.values()]

View File

@ -0,0 +1,81 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo } from 'react'
import PropTypes from 'prop-types'
import { EyeAlt } from 'iconoir-react'
import { MenuItem, MenuList, Stack } from '@mui/material'
import HeaderPopover from 'client/components/Header/Popover'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
/**
* Render all selected sorters.
*
* @returns {object} Component JSX
*/
const ChangeViewTable = memo(({ tableViews = {} }) => {
// Set click action
const handleClick = (name) => {
tableViews.onClick(name)
}
// Get view types
const typeViews = Object.entries(tableViews.views)
return (
<Stack direction="row" gap="0.5em" flexWrap="wrap">
<HeaderPopover
id="changeviewtable-by-button"
icon={<EyeAlt />}
headerTitle={Tr(T['acls.table.types.title'])}
buttonLabel={Tr(T['acls.table.types.button'])}
buttonProps={{
'data-cy': 'changeviewtable-by-button',
disableElevation: true,
variant: 'outlined',
color: 'secondary',
}}
popperProps={{ placement: 'bottom-end' }}
>
{() => (
<MenuList data-cy="change-view-list">
{typeViews.map(([key, value]) => (
<MenuItem
key={key}
onClick={() => handleClick(value.type)}
data-cy={key}
>
{Tr(value.name)}
</MenuItem>
))}
</MenuList>
)}
</HeaderPopover>
</Stack>
)
})
ChangeViewTable.propTypes = {
preFilteredRows: PropTypes.array,
state: PropTypes.object,
tableViews: PropTypes.object,
}
ChangeViewTable.displayName = 'ChangeViewTable'
export default ChangeViewTable

View File

@ -22,6 +22,7 @@ import GlobalFilter from 'client/components/Tables/Enhanced/Utils/GlobalFilter'
import GlobalSearch from 'client/components/Tables/Enhanced/Utils/GlobalSearch'
import GlobalSelectedRows from 'client/components/Tables/Enhanced/Utils/GlobalSelectedRows'
import GlobalSort from 'client/components/Tables/Enhanced/Utils/GlobalSort'
import ChangeViewTable from 'client/components/Tables/Enhanced/Utils/ChangeViewTable'
import TimeFilter from 'client/components/Tables/Enhanced/Utils/TimeFilter'
export * from 'client/components/Tables/Enhanced/Utils/GlobalActions/Action'
@ -37,6 +38,7 @@ export {
GlobalSelectedRows,
GlobalSort,
TimeFilter,
ChangeViewTable,
// Constants
LABEL_COLUMN_ID,
}

View File

@ -40,6 +40,7 @@ import {
GlobalSearch,
GlobalSelectedRows,
GlobalSort,
ChangeViewTable,
LABEL_COLUMN_ID,
} from 'client/components/Tables/Enhanced/Utils'
import Pagination from 'client/components/Tables/Enhanced/pagination'
@ -76,6 +77,7 @@ const EnhancedTable = ({
messages = [],
dataDepend,
readOnly = false,
tableViews,
}) => {
const styles = EnhancedTableStyles({
readOnly: readOnly,
@ -295,6 +297,7 @@ const EnhancedTable = ({
)}
<GlobalFilter {...useTableProps} />
{!disableGlobalSort && <GlobalSort {...useTableProps} />}
{tableViews && <ChangeViewTable tableViews={tableViews} />}
</div>
{/* SELECTED ROWS */}
{displaySelectedRows && !readOnly && (
@ -431,6 +434,7 @@ EnhancedTable.propTypes = {
messages: PropTypes.array,
dataDepend: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
readOnly: PropTypes.bool,
tableViews: PropTypes.object,
}
export * from 'client/components/Tables/Enhanced/Utils'

View File

@ -31,7 +31,7 @@ const Row = ({ original, value, ...props }) => {
const { color: stateColor, name: stateName } = ZoneModel.getState(original)
return (
<div {...props}>
<div {...props} data-cy={`zone-${ID}`}>
<div className={classes.main}>
<div className={classes.title}>
<StatusCircle color={stateColor} tooltip={stateName} />

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import ACLsTable from 'client/components/Tables/ACLs'
import AllImagesTable from 'client/components/Tables/AllImages'
import BackupJobsTable from 'client/components/Tables/BackupJobs'
import BackupsTable from 'client/components/Tables/Backups'
@ -46,6 +47,7 @@ import ZonesTable from 'client/components/Tables/Zones'
export * from 'client/components/Tables/Enhanced/Utils'
export {
ACLsTable,
AllImagesTable,
BackupJobsTable,
BackupsTable,

View File

@ -14,7 +14,14 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
// File with constants about ACLs
import { T } from 'client/constants'
// ACL actions
export const ACL_ACTIONS = {
CREATE_DIALOG: 'create_dialog',
CREATE_DIALOG_STRING: 'create_dialog_string',
DELETE: 'delete',
}
// Types of id definition
export const ACL_TYPE_ID = {
@ -32,6 +39,13 @@ export const ACL_ID = {
'%': 0x800000000,
}
export const ACL_USERS = {
INDIVIDUAL: { type: 'INDIVIDUAL', id: '#', value: 0x100000000 },
GROUP: { type: 'GROUP', id: '@', value: 0x200000000 },
ALL: { type: 'ALL', id: '*', value: 0x400000000 },
CLUSTER: { type: 'CLUSTER', id: '%', value: 0x800000000 },
}
// Hex values for different resource types
export const ACL_RESOURCES = {
VM: { name: 'VM', value: 0x1000000000n },
@ -62,3 +76,31 @@ export const ACL_RIGHTS = {
ADMIN: { name: 'ADMIN', value: 0x4 },
CREATE: { name: 'CREATE', value: 0x8 },
}
// type of table views
export const ACL_TABLE_VIEWS = {
ICONS: {
type: 'ICONS',
name: T['acls.table.types.icons'],
},
NAMES: {
type: 'NAMES',
name: T['acls.table.types.names'],
},
CLI: {
type: 'CLI',
name: T['acls.table.types.cli'],
},
RESOURCES: {
type: 'RESOURCES',
name: T['acls.table.types.resources'],
},
RULE: {
type: 'RULE',
name: T['acls.table.types.rule'],
},
READABLERULE: {
type: 'READABLERULE',
name: T['acls.table.types.readablerule'],
},
}

View File

@ -141,6 +141,7 @@ export const INPUT_TYPES = {
TOGGLE: 'toggle',
DOCKERFILE: 'dockerfile',
UNITS: 'units',
LABEL: 'label',
}
export const DEBUG_LEVEL = {
@ -162,6 +163,7 @@ export const SOCKETS = {
/** @enum {string} Names of resource */
export const RESOURCE_NAMES = {
APP: 'marketplace-app',
ACL: 'acl',
BACKUP: 'backup',
CLUSTER: 'cluster',
DATASTORE: 'datastore',

View File

@ -21,3 +21,9 @@ export const RESTRICTED_ATTRIBUTES_TYPE = {
IMAGE: 'IMAGE_RESTRICTED_ATTR',
VNET: 'VNET_RESTRICTED_ATTR',
}
export const FEDERATION_TYPE = {
STANDALONE: 'STANDALONE',
MASTER: 'MASTER',
SLAVE: 'SLAVE',
}

View File

@ -396,8 +396,8 @@ module.exports = {
/* sections - network */
Network: 'Network',
Networks: 'Networks',
VirtualNetwork: 'Virtual network',
VirtualNetworks: 'Virtual networks',
VirtualNetwork: 'Virtual Network',
VirtualNetworks: 'Virtual Networks',
NetworkTemplate: 'Network Template',
NetworkTemplates: 'Network Templates',
NetworkTopology: 'Network topology',
@ -1563,4 +1563,115 @@ module.exports = {
'showback.button.calculateShowback': 'Calculate showback',
'showback.button.help.paragraph.1':
'Generate showback data to the interval selected in start and end date. After generate the showback data, you can access to the reports on the user or group Showback details. ',
/* system - acls */
'acls.table.actions.create.string': 'Create from string',
'acls.table.types.icons': 'Icons',
'acls.table.types.names': 'Names',
'acls.table.types.cli': 'CLI',
'acls.table.types.resources': 'Resources',
'acls.table.types.rule': 'Rule',
'acls.table.types.readablerule': 'Readable Rule',
'acls.table.types.button': 'Table view',
'acls.table.types.title': 'Change to view:',
'acls.table.card.rule.user.tooltip': 'Rule applies to user: %1$s',
'acls.table.card.rule.group.tooltip': 'Rule applies to group: %1$s',
'acls.table.card.rule.cluster.tooltip': 'Rule applies to cluster: %1$s',
'acls.table.card.rule.all.tooltip': 'Rule applies to all',
'acls.table.card.rule.zone.tooltip': 'Rule applies to zone: %1$s',
'acls.table.card.rule.zone.tooltip.all': 'Rule applies to all zones',
'acls.table.card.resources.individual.tooltip':
'Resources with identifier: %1$s',
'acls.table.card.resources.group.tooltip': 'Resources owned by group: %1$s',
'acls.table.card.resources.cluster.tooltip':
'Resources owned by cluster: %1$s',
'acls.table.card.resources.all.tooltip': 'Resources owned by all',
'acls.table.filter.string': 'String rule',
'acls.table.filter.resources.user.id': 'Resource identifier id',
'acls.table.filter.resources.user.name': 'Resource identifier name',
'acls.table.filter.resources.user.type': 'Resource identifier type',
'acls.table.filter.user.id': 'User identifier id',
'acls.table.filter.user.name': 'User identifier name',
'acls.table.filter.user.type': 'User identifier type',
'acls.table.filter.zone.id': 'Zone identifier id',
'acls.table.filter.zone.name': 'Zone identifier name',
'acls.table.filter.zone.type': 'Zone identifier type',
'acls.form.create.user.title': 'Users',
'acls.form.create.user.info':
'Select whom the rule will apply. Could be an individual user, a group of users or all users.',
'acls.form.create.user.individual': 'Select which user to apply the rule to',
'acls.form.create.user.group': 'Select which group to apply the rule to',
'acls.form.create.resources.title': 'Resources',
'acls.form.create.resources.info': 'Select affected resources by the rule.',
'acls.form.create.resources.vm': 'Virtual Machine',
'acls.form.create.resources.vmtemplate': 'Virtual Machine Template',
'acls.form.create.resources.vnettemplate': 'Virtual Network Template',
'acls.form.create.resourcesUser.title': 'Resource identifier',
'acls.form.create.resourcesUser.info':
'Select resource owners. Could be an individual user, a group of users, a cluster or all users.',
'acls.form.create.resourcesUser.individual':
'Enter the number of the identifier',
'acls.form.create.resourcesUser.group':
'Select which group is the owner of the resources',
'acls.form.create.resourcesUser.cluster':
'Select which cluster is the owner of the resources',
'acls.form.create.resourcesUser.identifier': 'Identifier',
'acls.form.create.rights.title': 'Rights',
'acls.form.create.rights.info':
'Select the allowed operations that this rule will enable.',
'acls.form.create.zone.title': 'Zone',
'acls.form.create.zone.info':
'Select the zone where the rule will apply. Optional unless OpenNebula is configured in a federation.',
'acls.form.create.zone.zone': 'Select which zone the rule will apply.',
'acls.form.create.summary.title': 'Summary',
'acls.form.create.summary.info.rule': 'Rule that will be created: ',
'acls.form.create.summary.info.translation': 'That means: ',
'acls.form.create.stringEditor.title': 'String ACL rule',
'acls.form.create.stringEditor.info':
'Enter the string rule that will be created. Remember that a rule it is composed by four different components:',
'acls.form.create.stringEditor.info.user.title': 'User: ',
'acls.form.create.stringEditor.info.user.info':
'Composed by an ID definition (#<id> for individual user, @<id> for groups and * for all).',
'acls.form.create.stringEditor.info.resource.title': 'Resources: ',
'acls.form.create.stringEditor.info.resource.info':
"Composed by a list of '+' separated resource types, '/' and an ID definition (#<id> for individual resource, @<id> for groups, %<id> for clusters and * for all).",
'acls.form.create.stringEditor.info.rights.title': 'Rights: ',
'acls.form.create.stringEditor.info.rights.info':
"Is a list of operations (USE, MANAGE, ADMIN and CREATE) separated by the '+' character.",
'acls.form.create.stringEditor.info.zone.title': 'Zone: ',
'acls.form.create.stringEditor.info.zone.info':
'is an ID definition (#<id> for a zone and * for all) of the zones where the rule applies. This last part is optional, and can be ignored unless OpenNebula is configured in a federation.',
'acls.form.create.stringEditor.info.more':
'See OpenNebula documentation to get more details ',
'acls.form.create.stringEditor.info.more.link': 'about ACL rules.',
'acls.translate.rule': 'Rule',
'acls.translate.user.id': 'allow user with id',
'acls.translate.user.group': 'allows users in the group',
'acls.translate.user.all': 'allows all users',
'acls.translate.rights': 'the right to perform',
'acls.translate.and': 'and',
'acls.translate.operation': 'operation',
'acls.translate.operations': 'operations',
'acls.translate.over': 'over',
'acls.translate.overall': 'over all',
'acls.translate.resource.id': 'with identifier',
'acls.translate.resource.group': 'in the group',
'acls.translate.resource.cluster': 'in the cluster',
'acls.translate.zone.id': 'in the zone',
'acls.translate.zone.all': 'in all zones',
'acls.translate.error': 'ACL rule is NOT valid',
Identifier: 'Identifier',
Rights: 'Rights',
}

View File

@ -0,0 +1,128 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement } from 'react'
import PropTypes from 'prop-types'
import {
DefaultFormStepper,
SkeletonStepsForm,
} from 'client/components/FormStepper'
import { CreateForm } from 'client/components/Forms/ACLs'
import { createAclObjectFromString } from 'client/models/ACL'
import systemApi from 'client/features/OneApi/system'
import { useAllocateAclMutation } from 'client/features/OneApi/acl'
import { useGeneralApi } from 'client/features/General'
import { PATH } from 'client/apps/sunstone/routesOne'
import { useHistory, useLocation } from 'react-router'
import { useGetUsersQuery } from 'client/features/OneApi/user'
import { useGetGroupsQuery } from 'client/features/OneApi/group'
import { useGetClustersQuery } from 'client/features/OneApi/cluster'
import { useGetZonesQuery } from 'client/features/OneApi/zone'
import { useSystemData } from 'client/features/Auth'
const _ = require('lodash')
/**
* Displays the creation form for a ACL rule.
*
* @returns {ReactElement} - The ACL form component
*/
function CreateACLs() {
const { state: fromString } = useLocation()
// General api for enqueue
const { enqueueSuccess, enqueueError } = useGeneralApi()
// Get ONE config
const { oneConfig } = useSystemData()
// Get views
const { data: views } = systemApi.useGetSunstoneAvalaibleViewsQuery()
// Get version to show links to documentation
const { data: version } = systemApi.useGetOneVersionQuery()
// Get list of all users to add in the acl info the name of the user
const { data: users } = useGetUsersQuery()
// Get list of all groups to add in the acl info the name of the group
const { data: groups } = useGetGroupsQuery()
// Get list of all clusters to add in the acl info the name of the cluster
const { data: clusters } = useGetClustersQuery()
// Get list of all zones to add in the acl info the name of the zone
const { data: zones } = useGetZonesQuery()
// Operation to create ACL
const [createAcl] = useAllocateAclMutation()
const history = useHistory()
const onSubmit = async (props) => {
try {
// Create acl rule
const idAcl = await createAcl(
createAclObjectFromString(props?.string)
).unwrap()
if (idAcl) {
// Success message
enqueueSuccess(`ACL rule created - #${idAcl}`)
// Go to ACL list
history.push(PATH.SYSTEM.ACLS.LIST)
}
} catch (error) {
enqueueError('Error creating ACL rule')
}
}
return views &&
version &&
users &&
groups &&
clusters &&
zones &&
!_.isEmpty(oneConfig) ? (
<CreateForm
onSubmit={onSubmit}
stepProps={{
views,
version,
fromString,
users,
groups,
clusters,
zones,
oneConfig,
}}
fallback={<SkeletonStepsForm />}
>
{(config) => <DefaultFormStepper {...config} />}
</CreateForm>
) : (
<SkeletonStepsForm />
)
}
CreateACLs.propTypes = {
string: PropTypes.string,
}
export default CreateACLs

View File

@ -0,0 +1,83 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import ACLActions from 'client/components/Tables/ACLs/actions'
import { ACLsTable } from 'client/components/Tables'
/**
* Displays a list of Groups with a split pane between the list and selected row(s).
*
* @returns {ReactElement} Groups list and selected row(s)
*/
function ACLs() {
const [selectedRows, onSelectedRowsChange] = useState(() => [])
const hasSelectedRows = selectedRows?.length > 0
const actions = ACLActions()
return (
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<ACLsTable
onSelectedRowsChange={onSelectedRowsChange}
globalActions={actions}
/>
{hasSelectedRows && (
<>
<GutterComponent direction="row" track={1} />
<GroupedTags tags={selectedRows} />
</>
)}
</Box>
)}
</SplitPane>
)
}
/**
* Displays a list of tags that represent the selected rows.
*
* @param {Row[]} tags - Row(s) to display as tags
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}
/>
</Stack>
))
GroupedTags.propTypes = { tags: PropTypes.array }
GroupedTags.displayName = 'GroupedTags'
export default ACLs

View File

@ -36,7 +36,7 @@ import {
import { CreateForm, UpdateForm } from 'client/components/Forms/Group'
import { PATH } from 'client/apps/sunstone/routesOne'
import { createStringACL, createAclObjectFromString } from 'client/models/acl'
import { createStringACL, createAclObjectFromString } from 'client/models/ACL'
import { ACL_RIGHTS, ACL_TYPE_ID } from 'client/constants'
import systemApi from 'client/features/OneApi/system'
@ -159,7 +159,7 @@ function CreateGroup() {
// Go to groups list
history.push(PATH.SYSTEM.GROUPS.LIST)
} catch (error) {
enqueueError('Error creating group: ' + error.message)
enqueueError('Error creating group')
}
}

View File

@ -15,76 +15,152 @@
* ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/utils/constants/commands/acl'
import { oneApi, ONE_RESOURCES_POOL } from 'client/features/OneApi'
import userApi from 'client/features/OneApi/user'
import groupApi from 'client/features/OneApi/group'
import clusterApi from 'client/features/OneApi/cluster'
import zoneApi from 'client/features/OneApi/zone'
import { Acl } from 'client/constants'
import { aclFromString } from 'client/models/ACL'
const _ = require('lodash')
const { ACL_POOL } = ONE_RESOURCES_POOL
const basicEndpoints = (builder) => ({
getAcls: builder.query({
/**
* Retrieves information for all the acls in the pool.
*
* @returns {Acl[]} Get list of acls
* @throws Fails when response isn't code 200
*/
query: () => {
const name = Actions.ACL_INFO
const command = { name, ...Commands[name] }
return { command }
},
transformResponse: (data) => [data?.ACL_POOL?.ACL ?? []].flat(),
providesTags: (acls) =>
acls
? [...acls.map(({ ID }) => ({ type: ACL_POOL, id: `${ID}` })), ACL_POOL]
: [ACL_POOL],
}),
allocateAcl: builder.mutation({
/**
* Allocates a new acl in OpenNebula.
*
* @param {object} params - Request parameters
* @param {string} params.user - User for the new acl
* @param {string} params.resource - Resources for the new acl
* @param {string} params.right - Rights for the new acl
* @returns {number} The allocated Acl id
* @throws Fails when response isn't code 200
*/
query: (params) => {
const name = Actions.ACL_ADDRULE
const command = { name, ...Commands[name] }
return { params, command }
},
invalidatesTags: [ACL_POOL],
}),
removeAcl: builder.mutation({
/**
* Deletes the given acl from the pool.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Acl id
* @returns {number} Acl id
* @throws Fails when response isn't code 200
*/
query: (params) => {
const name = Actions.ACL_DELRULE
const command = { name, ...Commands[name] }
return { params, command }
},
invalidatesTags: [ACL_POOL],
}),
})
const extendedEnpoints = (builder) => ({
getAclsExtended: builder.query({
/**
* Retrieves information for all the acls in the pool adding the names of users, groups, clusters and zones.
*
* @param {object} params - Request parameters
* @param {object} props - Utils to use on queryFn function
* @param {Function} props.dispatch - Function to dispatch queries
* @returns {Acl[]} Get list of acls
* @throws Fails when response isn't code 200
*/
queryFn: async (params = {}, { dispatch }) => {
try {
// Get users
const users = await dispatch(
userApi.endpoints.getUsers.initiate(undefined, { forceRefetch: true })
).unwrap()
// Get groups
const groups = await dispatch(
groupApi.endpoints.getGroups.initiate(undefined, {
forceRefetch: true,
})
).unwrap()
// Get clusters
const clusters = await dispatch(
clusterApi.endpoints.getClusters.initiate(undefined, {
forceRefetch: true,
})
).unwrap()
// Get zones
const zones = await dispatch(
zoneApi.endpoints.getZones.initiate(undefined, { forceRefetch: true })
).unwrap()
// Get acls
const acls = await dispatch(
aclApi.endpoints.getAcls.initiate(undefined, { forceRefetch: true })
).unwrap()
const data = _.orderBy(
acls,
[(item) => parseInt(item.ID)],
['desc']
).map((acl) => ({
ID: acl.ID,
...aclFromString(acl.STRING, users, groups, clusters, zones),
}))
// Return data
return {
data,
}
} catch (error) {
return { error }
}
},
providesTags: (acls) =>
acls
? [...acls.map(({ ID }) => ({ type: ACL_POOL, id: `${ID}` })), ACL_POOL]
: [ACL_POOL],
}),
})
const aclApi = oneApi.injectEndpoints({
endpoints: (builder) => ({
getAcls: builder.query({
/**
* Retrieves information for all the acls in the pool.
*
* @returns {Acl[]} Get list of acls
* @throws Fails when response isn't code 200
*/
query: () => {
const name = Actions.ACL_INFO
const command = { name, ...Commands[name] }
return { command }
},
transformResponse: (data) => [data?.ACL_POOL?.ACL ?? []].flat(),
providesTags: (groups) =>
groups
? [
...groups.map(({ ID }) => ({ type: ACL_POOL, id: `${ID}` })),
ACL_POOL,
]
: [ACL_POOL],
}),
allocateAcl: builder.mutation({
/**
* Allocates a new acl in OpenNebula.
*
* @param {object} params - Request parameters
* @param {string} params.user - User for the new acl
* @param {string} params.resource - Resources for the new acl
* @param {string} params.right - Rights for the new acl
* @returns {number} The allocated Acl id
* @throws Fails when response isn't code 200
*/
query: (params) => {
const name = Actions.ACL_ADDRULE
const command = { name, ...Commands[name] }
return { params, command }
},
invalidatesTags: [ACL_POOL],
}),
removeAcl: builder.mutation({
/**
* Deletes the given acl from the pool.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Acl id
* @returns {number} Acl id
* @throws Fails when response isn't code 200
*/
query: (params) => {
const name = Actions.ACL_DELRULE
const command = { name, ...Commands[name] }
return { params, command }
},
invalidatesTags: [ACL_POOL],
}),
...basicEndpoints(builder),
...extendedEnpoints(builder),
}),
})
export const {
// Queries
useGetAclQuery,
useGetAclsQuery,
useGetAclsExtendedQuery,
// Mutations
useAllocateAclMutation,

View File

@ -0,0 +1,453 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
// File to functions about ACL
import { ACL_TYPE_ID, ACL_RIGHTS, T, ACL_USERS } from 'client/constants'
import { parseAcl } from 'client/utils'
import { Tr } from 'client/components/HOC'
const _ = require('lodash')
/**
* Create an ACL object to send to the API.
*
* @param {string} user - The user value in hex value
* @param {string} resource - The resource value in hex value
* @param {string} rights - The rights value in hex value
* @param {string} zone - The zone value in hex value
* @returns {object} - The object to send to the API
*/
export const createAclObject = (user, resource, rights, zone) => {
// Create response
const response = {
user: user,
resource: resource,
right: rights,
}
// Add zone if exists
if (zone) {
response.zone = zone
}
// Return response
return response
}
/**
* Create an ACL object to sent to the API from a string rule like #5 HOST+VM/@12 INFO+CREATE+DELETE *.
*
* @param {string} rule - String rule
* @returns {object} - The object to send to the API
*/
export const createAclObjectFromString = (rule) => {
// Parse the rule to get values
const ret = parseAcl(rule)
// Create response
const response = {
user: ret[0],
resource: ret[1],
right: ret[2],
}
// Add zone if exists
if (ret.length === 4) {
response.zone = ret[3]
}
// Return response
return response
}
/**
* Create a string rule using the values from a form.
*
* @param {string} userType - Type of user, e.g. "#"
* @param {number} userId - The id of the user, e.g. 4
* @param {Array} resources - List of resources, e.g. ["VM,"TEMPLATE",IMAGE"]
* @param {string} resourcesIdType - The type of the resources identifier, e.g. "#"
* @param {number} resourcesId - The id user of the resources, e.g. 4
* @param {Array} rights - List of rights, e.g. ["CREATE","USE"]
* @param {string} zoneType - Type of the zone, e.g. "#"
* @param {number} zoneId - The id of the user zone, e.g. 3
* @returns {string} - ACL string rule
*/
export const createStringACL = (
userType,
userId,
resources,
resourcesIdType,
resourcesId,
rights,
zoneType,
zoneId
) => {
// Define the string as empty string
let userString = ''
let resourcesString = ''
const resourcesIdentifierString = ''
let rightsString = ''
let zoneString
// User: Type of user identifier plus the user identifier, e.g. @105
if (userType) {
if (userType === ACL_TYPE_ID.ALL) {
userString += userType
} else if (
userType === ACL_TYPE_ID.INDIVIDUAL ||
userType === ACL_TYPE_ID.GROUP
)
userString += userType + (userId ?? '')
}
// Resources: List of resources separated by '+' plus the resources ID definition, e.g. VM+NET+IMAGE+TEMPLATE/#104
resources?.forEach((resource, index) => {
if (index < resources.length - 1) resourcesString += resource.name + '+'
else resourcesString += resource.name + '/'
})
if (resourcesIdType) {
if (resourcesIdType === ACL_TYPE_ID.ALL) {
resourcesString += resourcesIdType
} else if (
resourcesIdType === ACL_TYPE_ID.INDIVIDUAL ||
resourcesIdType === ACL_TYPE_ID.GROUP ||
resourcesIdType === ACL_TYPE_ID.CLUSTER
) {
resourcesString += resourcesIdType + (resourcesId ?? '')
}
}
// Rights: List of rights separated by '+', e.g. CREATE+USE
rights?.forEach((right, index) => {
if (index < rights.length - 1) rightsString += ACL_RIGHTS[right].name + '+'
else rightsString += ACL_RIGHTS[right].name
})
// Zone: Type of zone identifier plus the zone identifier, e.g. #44
if (zoneType) {
zoneString = zoneType + (zoneId ?? '')
}
// Return the ACL string
return (
userString +
' ' +
resourcesString +
resourcesIdentifierString +
' ' +
rightsString +
(zoneString ? ' ' + zoneString : '')
)
}
/**
* Check if a ACL string has the correct format.
*
* @param {string} rule - ACL rule
* @returns {boolean} - If the rule it's ok or not
*/
export const validACL = (rule) => {
// Regular expression for user component
const userRegex = /([#@]\d+|[*])/
// Regular expression for resources component
const resourceTypePattern =
/(VM|HOST|NET|IMAGE|USER|TEMPLATE|GROUP|DATASTORE|CLUSTER|DOCUMENT|ZONE|SECGROUP|VDC|VROUTER|MARKETPLACE|MARKETPLACEAPP|VMGROUP|VNTEMPLATE|BACKUPJOB)/
const otherResourcesTypePattern = new RegExp(
`(\\+${resourceTypePattern.source})*`
)
const resourcesUserRegex = /([#@%]\d+|[*])/
const resourcesRegex = new RegExp(
`${resourceTypePattern.source}${otherResourcesTypePattern.source}\\/${resourcesUserRegex.source}`
)
// Regular expression for rights component
const rightsTypePattern = /(USE|MANAGE|ADMIN|CREATE)/
const otherRightsTypePattern = new RegExp(`(\\+${rightsTypePattern.source})*`)
const rightsRegex = new RegExp(
`${rightsTypePattern.source}${otherRightsTypePattern.source}`
)
// Regular expression for zone component
const zoneRegex = /(\*|#\d+)/
// ACL regular expression
const aclRuleRegex = new RegExp(
`^${userRegex.source}\\s${resourcesRegex.source}\\s${rightsRegex.source}(\\s${zoneRegex.source})?$`
)
// Check rule
return aclRuleRegex.test(rule)
}
/**
* Create a human readable message with the meaning of the rule.
*
* @param {string} rule - The ACL sring rule
* @param {Array} users - List of users
* @param {Array} groups - List of groups
* @param {Array} clusters - List of clusters
* @param {Array} zones - List of zones
* @returns {string} - The meaning of the rule
*/
export const translateACL = (rule, users, groups, clusters, zones) => {
// Create a readable object from the string rule
const acl = aclFromString(rule, users, groups, clusters, zones)
// Define the message
let message = Tr(T['acls.translate.rule']) + ' '
// User info
if (
acl.USER?.type &&
acl.USER.type === ACL_USERS.INDIVIDUAL.type &&
acl.USER?.id
) {
message += Tr(T['acls.translate.user.id']) + ' '
if (acl.USER.id) {
message += acl.USER.id + ' '
}
if (acl.USER.name) {
message += ' (' + acl.USER.name + ') '
}
} else if (
acl.USER?.type &&
acl.USER.type === ACL_USERS.GROUP.type &&
acl.USER?.id
) {
message += Tr(T['acls.translate.user.group']) + ' ' + acl.USER.id + ' '
if (acl.USER.name) {
message += ' (' + acl.USER.name + ') '
}
} else if (acl.USER?.type && acl.USER.type === ACL_USERS.ALL.type) {
message += Tr(T['acls.translate.user.all']) + ' '
}
// Rights info
message += Tr(T['acls.translate.rights']) + ' '
const rights = acl.RIGHTS?.rights
rights?.forEach((right, index) => {
if (index === rights.length - 1) {
message += right
} else {
message += right + ' ' + Tr(T['acls.translate.and']) + ' '
}
})
if (rights?.length > 1) {
message += ' ' + Tr(T['acls.translate.operations']) + ' '
} else {
message += ' ' + Tr(T['acls.translate.operation']) + ' '
}
// Resources info
if (
acl.RESOURCE?.identifier.type &&
acl.RESOURCE.identifier.type === ACL_USERS.INDIVIDUAL.type
) {
message += Tr(T['acls.translate.overall']) + ' '
} else {
message += Tr(T['acls.translate.over']) + ' '
}
acl.RESOURCE?.resources.forEach((resource, index) => {
if (index === acl.RESOURCE.resources.length - 1) {
message += resource
} else {
message += resource + ' ' + Tr(T['acls.translate.and']) + ' '
}
})
// Resources identifier info
if (
acl.RESOURCE?.identifier.type &&
acl.RESOURCE.identifier.type === ACL_USERS.INDIVIDUAL.type &&
acl.RESOURCE?.identifier.id
) {
message +=
' ' +
Tr(T['acls.translate.resource.id']) +
' ' +
acl.RESOURCE.identifier.id
} else if (
acl.RESOURCE?.identifier.type &&
acl.RESOURCE.identifier.type === ACL_USERS.GROUP.type &&
acl.RESOURCE?.identifier.id
) {
message +=
' ' +
Tr(T['acls.translate.resource.group']) +
' ' +
acl.RESOURCE.identifier.id
if (acl.RESOURCE.identifier.name) {
message += ' (' + acl.RESOURCE.identifier.name + ')'
}
} else if (
acl.RESOURCE?.identifier.type &&
acl.RESOURCE.identifier.type === ACL_USERS.CLUSTER.type &&
acl.RESOURCE?.identifier.id
) {
message +=
' ' +
Tr(T['acls.translate.resource.cluster']) +
' ' +
acl.RESOURCE.identifier.id
if (acl.RESOURCE.identifier.name) {
message += ' (' + acl.RESOURCE.identifier.name + ')'
}
}
// Zone info
if (acl.ZONE?.type && acl.ZONE?.type === ACL_USERS.INDIVIDUAL.type) {
message += ' ' + Tr(T['acls.translate.zone.id']) + ' ' + acl.ZONE.id
if (acl.ZONE.name) {
message += ' (' + acl.ZONE.name + ')'
}
} else if (acl.ZONE?.type === ACL_USERS.ALL.type) {
message += ' ' + Tr(T['acls.translate.zone.all'])
}
// Return message
return message
}
/**
* Create a readable object from an acl rule.
*
* @param {string} rule - ACL rule
* @param {Array} users - List of users
* @param {Array} groups - List of groups
* @param {Array} clusters - List of clusters
* @param {Array} zones - List of zones
* @returns {object} The ACL rule in a readable object
*/
export const aclFromString = (rule, users, groups, clusters, zones) => {
// Create object
const acl = {
STRING: rule,
}
// Split rule
const aclComponents = acl.STRING.split(' ')
// User
if (aclComponents[0]) {
// Get user info
const user = aclComponents[0]
// Set user values
const userType = _.find(ACL_USERS, { id: user.charAt(0) })?.type
const userId = user.length > 1 ? user.substring(1) : undefined
let username
if (userType === ACL_USERS.INDIVIDUAL.type && users) {
username = _.find(users, { ID: userId })?.NAME
} else if (userType === ACL_USERS.GROUP.type && groups) {
username = _.find(groups, { ID: userId })?.NAME
}
// Create user object
acl.USER = {
type: userType,
id: userId,
string: aclComponents[0],
name: username,
}
}
// Resources
if (aclComponents[1]) {
// Get resources info
const resourcesComponents = aclComponents[1].split('/')
const resources = resourcesComponents[0].split('+')
const resourcesIdentifier = resourcesComponents[1]
// Set resource user values
const resourceUserType = _.find(ACL_USERS, {
id: resourcesIdentifier.charAt(0),
})?.type
const resourceId =
resourcesIdentifier.length > 1
? resourcesIdentifier.substring(1)
: undefined
let resourceUsername
if (resourceUserType === ACL_USERS.GROUP.type && groups) {
resourceUsername = _.find(groups, {
ID: resourceId,
})?.NAME
} else if (resourceUserType === ACL_USERS.CLUSTER.type && clusters) {
resourceUsername = _.find(clusters, {
ID: resourceId,
})?.NAME
}
// Create resource object
acl.RESOURCE = {
resources: resources,
identifier: {
type: resourceUserType,
id: resourceId,
string: resourcesIdentifier,
name: resourceUsername,
},
string: resourcesComponents,
}
}
// Rights
if (aclComponents[2]) {
// Get rights info
const rights = aclComponents[2].split('+')
acl.RIGHTS = {
rights: rights,
string: aclComponents[2],
}
}
// Zone
if (aclComponents[3]) {
// Create rights object
const zone = aclComponents[3]
if (zone) {
// Set zone values
const zoneType = _.find(ACL_USERS, { id: zone.charAt(0) }).type
const zoneId = zone.length > 1 ? zone.substring(1) : undefined
let zonename
if (zoneType === ACL_USERS.INDIVIDUAL.type && zones) {
zonename = _.find(zones, { ID: zoneId })?.NAME
}
if (zone) {
acl.ZONE = {
type: zoneType,
id: zoneId,
string: zone,
name: zonename,
}
}
}
}
// Return acl
return acl
}

View File

@ -1,131 +0,0 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
// File to functions about ACL
import { ACL_TYPE_ID, ACL_RIGHTS } from 'client/constants'
import { parseAcl } from 'client/utils'
/**
* Create an ACL object to send to the API.
*
* @param {string} user - The user value in hex value
* @param {string} resource - The resource value in hex value
* @param {string} rights - The rights value in hex value
* @param {string} zone - The zone value in hex value
* @returns {object} - The object to send to the API
*/
export const createAclObject = (user, resource, rights, zone) => {
// Create response
const response = {
user: user,
resource: resource,
right: rights,
}
// Add zone if exists
if (zone) {
response.zone = zone
}
// Return response
return response
}
/**
* Create an ACL object to sent to the API from a string rule like #5 HOST+VM/@12 INFO+CREATE+DELETE *.
*
* @param {string} rule - String rule
* @returns {object} - The object to send to the API
*/
export const createAclObjectFromString = (rule) => {
// Parse the rule to get values
const ret = parseAcl(rule)
// Create response
const response = {
user: ret[0],
resource: ret[1],
right: ret[2],
}
// Add zone if exists
if (ret.length === 4) {
response.zone = ret[3]
}
// Return response
return response
}
/**
* Create a string rule using the values from a form.
*
* @param {string} userType - Type of user, e.g. "#"
* @param {number} userId - The id of the user, e.g. 4
* @param {Array} resources - List of resources, e.g. ["VM,"TEMPLATE",IMAGE"]
* @param {string} resourcesIdType - The type of the resources identifier, e.g. "#"
* @param {number} resourcesId - The id user of the resources, e.g. 4
* @param {Array} rights - List of rights, e.g. ["CREATE","USE"]
* @param {string} zoneType - Type of the zone, e.g. "#"
* @param {number} zoneId - The id of the user zone, e.g. 3
* @returns {string} - ACL string rule
*/
export const createStringACL = (
userType,
userId,
resources,
resourcesIdType,
resourcesId,
rights,
zoneType,
zoneId
) => {
// Define the string as empty string
let acl = ''
// User: Type of user identifier plus the user identifier, e.g. @105
acl += userType === ACL_TYPE_ID.ALL ? userType + ' ' : userType + userId + ' '
// Resources: List of resources separated by '+' plus the resources ID definition, e.g. VM+NET+IMAGE+TEMPLATE/#104
resources.forEach((resource, index) => {
if (index < resources.length - 1) acl += resource.name + '+'
else acl += resource.name + '/'
})
acl +=
resourcesIdType === ACL_TYPE_ID.ALL
? resourcesIdType + ' '
: resourcesIdType + resourcesId + ' '
// Rights: List of rights separated by '+', e.g. CREATE+USE
rights.forEach((right, index) => {
if (index < rights.length - 1) acl += ACL_RIGHTS[right].name + '+'
else
acl +=
zoneType && zoneId
? ACL_RIGHTS[right].name + ' '
: ACL_RIGHTS[right].name
})
// Zone: Type of zone identifier plus the zone identifier, e.g. #44
if (zoneType && zoneId) {
acl += zoneType + zoneId
}
// Return the ACL string
return acl
}

View File

@ -17,7 +17,7 @@ import templateToObject from 'client/utils/parser/templateToObject'
import parseApplicationToForm from 'client/utils/parser/parseApplicationToForm'
import parseFormToApplication from 'client/utils/parser/parseFormToApplication'
import parseFormToDeployApplication from 'client/utils/parser/parseFormToDeployApplication'
import parseAcl from 'client/utils/parser/parseACL'
import { parseAcl } from 'client/utils/parser/parseACL'
export {
templateToObject,

View File

@ -25,7 +25,7 @@ import { ACL_ID, ACL_RESOURCES, ACL_RIGHTS } from 'client/constants'
* @param {string} rule - The ACL rule
* @returns {number} - The hex value for the four components of a rule (user, resources, rights and zone)
*/
const parseAcl = (rule) => {
export const parseAcl = (rule) => {
// Get each component
const ruleComponents = rule.split(' ')
@ -140,6 +140,3 @@ const calculateIds = (id) => {
// Return the integer id value
return idValue
}
// Export parseRule function
export default parseAcl

View File

@ -47,6 +47,10 @@ module.exports = {
from: postBody,
default: '0x1',
},
zone: {
from: postBody,
default: '0x100000000',
},
},
},
[ACL_DELRULE]: {