From c9ed2fe5dff07da9aefe942c51cb8489fefcdbc7 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 23 Nov 2023 14:19:53 +0100 Subject: [PATCH] F OpenNebula#6332: Restricted attributes on images and vnets (#2832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tino Vázquez --- .../components/Buttons/AddressRangeActions.js | 232 ++++++++++-------- .../CreateForm/Steps/AdvancedOptions/index.js | 17 +- .../Steps/AdvancedOptions/schema.js | 32 ++- .../Image/CreateForm/Steps/General/index.js | 17 +- .../Image/CreateForm/Steps/General/schema.js | 39 ++- .../Forms/VNetwork/AddRangeForm/content.js | 18 +- .../Forms/VNetwork/AddRangeForm/schema.js | 54 ++-- .../Steps/ExtraConfiguration/addresses.js | 25 +- .../Steps/ExtraConfiguration/context/index.js | 17 +- .../ExtraConfiguration/context/schema.js | 55 ++++- .../Steps/ExtraConfiguration/index.js | 24 +- .../Steps/ExtraConfiguration/qos/index.js | 12 +- .../Steps/ExtraConfiguration/qos/schema.js | 43 ++-- .../Steps/ExtraConfiguration/schema.js | 8 +- .../Steps/ExtraConfiguration/security.js | 20 +- .../CreateForm/Steps/General/index.js | 22 +- .../CreateForm/Steps/General/schema.js | 30 ++- .../components/Tables/Enhanced/index.js | 12 +- .../components/Tables/Enhanced/styles.js | 7 +- .../components/Tabs/VNetwork/Address.js | 33 ++- .../components/Tabs/VNetwork/Security.js | 53 ++-- .../client/components/Tabs/VNetwork/index.js | 12 +- src/fireedge/src/client/constants/system.js | 6 + .../src/client/containers/Images/Create.js | 18 +- .../containers/VirtualNetworks/Create.js | 17 +- .../src/client/utils/restrictedAttributes.js | 25 ++ src/fireedge/src/client/utils/schema.js | 30 ++- .../src/server/routes/api/system/functions.js | 2 + 28 files changed, 618 insertions(+), 262 deletions(-) diff --git a/src/fireedge/src/client/components/Buttons/AddressRangeActions.js b/src/fireedge/src/client/components/Buttons/AddressRangeActions.js index 8092c48892..ac7d84aa68 100644 --- a/src/fireedge/src/client/components/Buttons/AddressRangeActions.js +++ b/src/fireedge/src/client/components/Buttons/AddressRangeActions.js @@ -29,123 +29,155 @@ import { AddRangeForm } from 'client/components/Forms/VNetwork' import { jsonToXml } from 'client/models/Helper' import { Tr, Translate } from 'client/components/HOC' -import { T, VN_ACTIONS } from 'client/constants' +import { T, VN_ACTIONS, RESTRICTED_ATTRIBUTES_TYPE } from 'client/constants' -const AddAddressRangeAction = memo(({ vnetId, onSubmit }) => { - const [addAR] = useAddRangeToVNetMutation() +import { hasRestrictedAttributes } from 'client/utils' - const handleAdd = async (formData) => { - if (onSubmit && typeof onSubmit === 'function') { - return await onSubmit(formData) +const AddAddressRangeAction = memo( + ({ vnetId, onSubmit, oneConfig, adminGroup }) => { + const [addAR] = useAddRangeToVNetMutation() + + const handleAdd = async (formData) => { + if (onSubmit && typeof onSubmit === 'function') { + return await onSubmit(formData) + } + + const template = jsonToXml({ AR: formData }) + await addAR({ id: vnetId, template }).unwrap() } - const template = jsonToXml({ AR: formData }) - await addAR({ id: vnetId, template }).unwrap() - } - - return ( - , - label: T.AddressRange, - variant: 'outlined', - }} - options={[ - { - dialogProps: { - title: T.AddressRange, - dataCy: 'modal-add-ar', - }, - form: AddRangeForm, - onSubmit: handleAdd, - }, - ]} - /> - ) -}) - -const UpdateAddressRangeAction = memo(({ vnetId, ar, onSubmit }) => { - const [updateAR] = useUpdateVNetRangeMutation() - const { AR_ID } = ar - - const handleUpdate = async (formData) => { - if (onSubmit && typeof onSubmit === 'function') { - return await onSubmit(formData) + const formConfig = { + stepProps: { + vnetId, + oneConfig, + adminGroup, + restrictedAttributesType: RESTRICTED_ATTRIBUTES_TYPE.VNET, + nameParentAttribute: 'AR', + }, } - const template = jsonToXml({ AR: formData }) - await updateAR({ id: vnetId, template }) - } - - return ( - , - tooltip: T.Edit, - }} - options={[ - { - dialogProps: { - title: `${Tr(T.AddressRange)}: #${AR_ID}`, - dataCy: 'modal-update-ar', + return ( + , + label: T.AddressRange, + variant: 'outlined', + }} + options={[ + { + dialogProps: { + title: T.AddressRange, + dataCy: 'modal-add-ar', + }, + form: () => AddRangeForm(formConfig), + onSubmit: handleAdd, }, - form: () => - AddRangeForm({ - initialValues: ar, - stepProps: { isUpdate: !onSubmit && AR_ID !== undefined }, - }), - onSubmit: handleUpdate, - }, - ]} - /> - ) -}) + ]} + /> + ) + } +) -const DeleteAddressRangeAction = memo(({ vnetId, ar, onSubmit }) => { - const [removeAR] = useRemoveRangeFromVNetMutation() - const { AR_ID } = ar +const UpdateAddressRangeAction = memo( + ({ vnetId, ar, onSubmit, oneConfig, adminGroup }) => { + const [updateAR] = useUpdateVNetRangeMutation() + const { AR_ID } = ar - const handleRemove = async () => { - if (onSubmit && typeof onSubmit === 'function') { - return await onSubmit(AR_ID) + const handleUpdate = async (formData) => { + if (onSubmit && typeof onSubmit === 'function') { + return await onSubmit(formData) + } + + const template = jsonToXml({ AR: formData }) + await updateAR({ id: vnetId, template }) } - await removeAR({ id: vnetId, address: AR_ID }) - } - - return ( - , - tooltip: T.Delete, - }} - options={[ - { - isConfirmDialog: true, - dialogProps: { - title: ( - <> - - {`: #${AR_ID}`} - - ), - children:

{Tr(T.DoYouWantProceed)}

, + return ( + , + tooltip: T.Edit, + }} + options={[ + { + dialogProps: { + title: `${Tr(T.AddressRange)}: #${AR_ID}`, + dataCy: 'modal-update-ar', + }, + form: () => + AddRangeForm({ + initialValues: ar, + stepProps: { + isUpdate: !onSubmit && AR_ID !== undefined, + oneConfig, + adminGroup, + restrictedAttributesType: RESTRICTED_ATTRIBUTES_TYPE.VNET, + nameParentAttribute: 'AR', + }, + }), + onSubmit: handleUpdate, }, - onSubmit: handleRemove, - }, - ]} - /> - ) -}) + ]} + /> + ) + } +) + +const DeleteAddressRangeAction = memo( + ({ vnetId, ar, onSubmit, oneConfig, adminGroup }) => { + const [removeAR] = useRemoveRangeFromVNetMutation() + const { AR_ID } = ar + + const handleRemove = async () => { + if (onSubmit && typeof onSubmit === 'function') { + return await onSubmit(AR_ID) + } + + await removeAR({ id: vnetId, address: AR_ID }) + } + + // Disable action if the disk has a restricted attribute on the template + const disabledAction = + !adminGroup && + hasRestrictedAttributes(ar, 'AR', oneConfig?.VNET_RESTRICTED_ATTR) + + return ( + , + tooltip: !disabledAction ? Tr(T.Detach) : Tr(T.DetachRestricted), + disabled: disabledAction, + }} + options={[ + { + isConfirmDialog: true, + dialogProps: { + title: ( + <> + + {`: #${AR_ID}`} + + ), + children:

{Tr(T.DoYouWantProceed)}

, + }, + onSubmit: handleRemove, + }, + ]} + /> + ) + } +) const ActionPropTypes = { vnetId: PropTypes.string, ar: PropTypes.object, onSubmit: PropTypes.func, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } AddAddressRangeAction.propTypes = ActionPropTypes diff --git a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/index.js b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/index.js index cac545a0b3..d98235ed2a 100644 --- a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/index.js +++ b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/index.js @@ -23,21 +23,28 @@ import { T } from 'client/constants' export const STEP_ID = 'advanced' -const Content = () => ( - +const Content = (oneConfig, adminGroup) => ( + ) /** * Advanced options create image. * + * @param {object} props - Step properties + * @param {object} props.oneConfig - Open Nebula configuration + * @param {boolean} props.adminGroup - If the user belongs to oneadmin group * @returns {object} Advanced options configuration step */ -const AdvancedOptions = () => ({ +const AdvancedOptions = ({ oneConfig, adminGroup }) => ({ id: STEP_ID, label: T.AdvancedOptions, - resolver: SCHEMA, + resolver: SCHEMA(oneConfig, adminGroup), optionsValidate: { abortEarly: false }, - content: Content, + content: () => Content(oneConfig, adminGroup), }) export default AdvancedOptions diff --git a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js index 50baad9a13..5dbd80eda3 100644 --- a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js +++ b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js @@ -14,8 +14,13 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import { string, object, ObjectSchema } from 'yup' -import { Field, arrayToOptions, getValidationFromFields } from 'client/utils' -import { T, INPUT_TYPES } from 'client/constants' +import { + Field, + arrayToOptions, + getValidationFromFields, + disableFields, +} from 'client/utils' +import { T, INPUT_TYPES, RESTRICTED_ATTRIBUTES_TYPE } from 'client/constants' import { IMAGE_LOCATION_TYPES, IMAGE_LOCATION_FIELD, @@ -156,18 +161,23 @@ export const FS = { } /** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {Field[]} Fields */ -export const FIELDS = [ - DEV_PREFIX, - DEVICE, - CUSTOM_DEV_PREFIX, - FORMAT_FIELD, - FS, - CUSTOM_FORMAT, -] +export const FIELDS = (oneConfig, adminGroup) => + disableFields( + [DEV_PREFIX, DEVICE, CUSTOM_DEV_PREFIX, FORMAT_FIELD, FS, CUSTOM_FORMAT], + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.IMAGE + ) /** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {ObjectSchema} Schema */ -export const SCHEMA = object(getValidationFromFields(FIELDS)) +export const SCHEMA = (oneConfig, adminGroup) => + object(getValidationFromFields(FIELDS(oneConfig, adminGroup))) diff --git a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/index.js b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/index.js index 9c5ac68678..0efec7de41 100644 --- a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/index.js +++ b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/index.js @@ -24,21 +24,28 @@ import { T } from 'client/constants' export const STEP_ID = 'general' -const Content = () => ( - +const Content = (oneConfig, adminGroup) => ( + ) /** * General configuration about VM Template. * + * @param {object} props - Step properties + * @param {object} props.oneConfig - Open Nebula configuration + * @param {boolean} props.adminGroup - If the user belongs to oneadmin group * @returns {object} General configuration step */ -const General = () => ({ +const General = ({ oneConfig, adminGroup }) => ({ id: STEP_ID, label: T.Configuration, - resolver: SCHEMA, + resolver: SCHEMA(oneConfig, adminGroup), optionsValidate: { abortEarly: false }, - content: Content, + content: () => Content(oneConfig, adminGroup), }) export default General diff --git a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/schema.js b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/schema.js index 7d04ff6170..e6c5098bea 100644 --- a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/schema.js +++ b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/General/schema.js @@ -19,6 +19,7 @@ import { arrayToOptions, getValidationFromFields, upperCaseFirst, + disableFields, } from 'client/utils' import { T, @@ -26,6 +27,7 @@ import { IMAGE_TYPES_STR, IMAGE_TYPES_FOR_IMAGES, UNITS, + RESTRICTED_ATTRIBUTES_TYPE, } from 'client/constants' export const IMAGE_LOCATION_TYPES = { @@ -193,22 +195,33 @@ export const SIZEUNIT = { } /** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {Field[]} Fields */ -export const FIELDS = [ - NAME, - DESCRIPTION, - TYPE, - PERSISTENT, - IMAGE_LOCATION_FIELD, - PATH_FIELD, - UPLOAD_FIELD, - SIZE, - SIZEUNIT, -] +export const FIELDS = (oneConfig, adminGroup) => + disableFields( + [ + NAME, + DESCRIPTION, + TYPE, + PERSISTENT, + IMAGE_LOCATION_FIELD, + PATH_FIELD, + UPLOAD_FIELD, + SIZE, + SIZEUNIT, + ], + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.IMAGE + ) /** - * @param {object} [stepProps] - Step props + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {ObjectSchema} Schema */ -export const SCHEMA = object(getValidationFromFields(FIELDS)) +export const SCHEMA = (oneConfig, adminGroup) => + object(getValidationFromFields(FIELDS(oneConfig, adminGroup))) diff --git a/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/content.js b/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/content.js index 88b11a72b4..2550f2922a 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/content.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/content.js @@ -32,9 +32,11 @@ export const CUSTOM_ATTRS_ID = 'custom-attributes' /** * @param {object} props - Props * @param {boolean} [props.isUpdate] - Is `true` the form will be filter immutable attributes + * @param {object} props.oneConfig - Open Nebula configuration + * @param {boolean} props.adminGroup - If the user belongs to oneadmin group * @returns {ReactElement} Form content component */ -const Content = ({ isUpdate }) => { +const Content = ({ isUpdate, oneConfig, adminGroup }) => { const { setValue } = useFormContext() const customAttrs = useWatch({ name: CUSTOM_ATTRS_ID }) || {} @@ -47,7 +49,13 @@ const Content = ({ isUpdate }) => { return ( - + { ) } -Content.propTypes = { isUpdate: PropTypes.bool } +Content.propTypes = { + isUpdate: PropTypes.bool, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} export default Content diff --git a/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/schema.js b/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/schema.js index 4a93de8198..3e1590877e 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/schema.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/AddRangeForm/schema.js @@ -23,8 +23,9 @@ import { REG_V4, REG_V6, REG_MAC, + disableFields, } from 'client/utils' -import { T, INPUT_TYPES } from 'client/constants' +import { T, INPUT_TYPES, RESTRICTED_ATTRIBUTES_TYPE } from 'client/constants' const AR_TYPES = { IP4: 'IP4', @@ -183,25 +184,50 @@ const ULA_PREFIX_FIELD = { } /** @type {Field[]} Fields */ -const FIELDS = [ - TYPE_FIELD, - IP_FIELD, - MAC_FIELD, - IP6_FIELD, - SIZE_FIELD, - GLOBAL_PREFIX_FIELD, - PREFIX_LENGTH_FIELD, - ULA_PREFIX_FIELD, -] +const FIELDS = (oneConfig, adminGroup) => + disableFields( + [ + TYPE_FIELD, + IP_FIELD, + MAC_FIELD, + IP6_FIELD, + SIZE_FIELD, + GLOBAL_PREFIX_FIELD, + PREFIX_LENGTH_FIELD, + ULA_PREFIX_FIELD, + ], + 'AR', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ) -const MUTABLE_FIELDS = [SIZE_FIELD] +/** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group + * @returns {Array} - Mutable fields + */ +const MUTABLE_FIELDS = (oneConfig, adminGroup) => + disableFields( + [SIZE_FIELD], + 'AR', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ) /** * @param {object} stepProps - Step props * @param {boolean} stepProps.isUpdate - If true the form is to update the AR + * @param {object} stepProps.oneConfig - Open Nebula configuration + * @param {boolean} stepProps.adminGroup - If the user belongs to oneadmin group * @returns {BaseSchema} Schema */ -const SCHEMA = ({ isUpdate }) => - getObjectSchemaFromFields([...(isUpdate ? MUTABLE_FIELDS : FIELDS)]) +const SCHEMA = ({ isUpdate, oneConfig, adminGroup }) => + getObjectSchemaFromFields([ + ...(isUpdate + ? MUTABLE_FIELDS(oneConfig, adminGroup) + : FIELDS(oneConfig, adminGroup)), + ]) export { FIELDS, MUTABLE_FIELDS, SCHEMA } diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/addresses.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/addresses.js index e0b8626b29..b48b9d52d1 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/addresses.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/addresses.js @@ -37,7 +37,7 @@ export const TAB_ID = 'AR' const mapNameFunction = mapNameByIndex('AR') -const AddressesContent = () => { +const AddressesContent = ({ oneConfig, adminGroup }) => { const { fields: addresses, remove, @@ -63,7 +63,11 @@ const AddressesContent = () => { return ( <> - + { vm={{}} ar={fakeValues} onSubmit={(updatedAr) => handleUpdate(updatedAr, index)} + oneConfig={oneConfig} + adminGroup={adminGroup} /> { ) } -const Content = ({ isUpdate }) => +AddressesContent.propTypes = { + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} + +const Content = ({ isUpdate, oneConfig, adminGroup }) => isUpdate ? ( ) : ( - + ) -Content.propTypes = { isUpdate: PropTypes.bool } +Content.propTypes = { + isUpdate: PropTypes.bool, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} /** @type {TabType} */ const TAB = { diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/index.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/index.js index 466ba4c619..863276d6e9 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/index.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/index.js @@ -14,7 +14,7 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import ContextIcon from 'iconoir-react/dist/Folder' - +import PropTypes from 'prop-types' import { TabType, STEP_ID as EXTRA_ID, @@ -25,20 +25,29 @@ import CustomAttributes from 'client/components/Forms/VNetwork/CreateForm/Steps/ import FormWithSchema from 'client/components/Forms/FormWithSchema' import { T } from 'client/constants' -const ContextContent = () => ( +const ContextContent = ({ oneConfig, adminGroup }) => ( <> - + ) +ContextContent.propTypes = { + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} + /** @type {TabType} */ const TAB = { id: 'context', name: T.Context, icon: ContextIcon, Content: ContextContent, - getError: (error) => FIELDS.some(({ name }) => error?.[name]), + getError: (error) => FIELDS().some(({ name }) => error?.[name]), } export default TAB diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/schema.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/schema.js index aaf8f2ad85..cf8d63dd25 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/schema.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/context/schema.js @@ -15,8 +15,19 @@ * ------------------------------------------------------------------------- */ import { string } from 'yup' -import { Field, arrayToOptions, getObjectSchemaFromFields } from 'client/utils' -import { T, INPUT_TYPES, VNET_METHODS, VNET_METHODS6 } from 'client/constants' +import { + Field, + arrayToOptions, + getObjectSchemaFromFields, + disableFields, +} from 'client/utils' +import { + T, + INPUT_TYPES, + VNET_METHODS, + VNET_METHODS6, + RESTRICTED_ATTRIBUTES_TYPE, +} from 'client/constants' /** @type {Field} Network address field */ const NETWORK_ADDRESS_FIELD = { @@ -96,15 +107,33 @@ const IP6_METHOD_FIELD = { validation: string().trim().notRequired(), } -export const FIELDS = [ - NETWORK_ADDRESS_FIELD, - NETWORK_MASK_FIELD, - GATEWAY_FIELD, - GATEWAY6_FIELD, - DNS_FIELD, - GUEST_MTU_FIELD, - METHOD_FIELD, - IP6_METHOD_FIELD, -] +/** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group + * @returns {Array} Fields + */ +export const FIELDS = (oneConfig, adminGroup) => + disableFields( + [ + NETWORK_ADDRESS_FIELD, + NETWORK_MASK_FIELD, + GATEWAY_FIELD, + GATEWAY6_FIELD, + DNS_FIELD, + GUEST_MTU_FIELD, + METHOD_FIELD, + IP6_METHOD_FIELD, + ], + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ) -export const SCHEMA = getObjectSchemaFromFields(FIELDS) +/** + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group + * @returns {object} Schema + */ +export const SCHEMA = (oneConfig, adminGroup) => + getObjectSchemaFromFields(FIELDS(oneConfig, adminGroup)) diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/index.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/index.js index 75ed9f886e..642fecfb41 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/index.js @@ -45,7 +45,7 @@ export const STEP_ID = 'extra' /** @type {TabType[]} */ export const TABS = [Addresses, Security, QoS, Context] -const Content = ({ isUpdate }) => { +const Content = ({ isUpdate, oneConfig, adminGroup }) => { const { watch, formState: { errors }, @@ -61,7 +61,14 @@ const Content = ({ isUpdate }) => { ...section, name, label: , - renderContent: () => , + renderContent: () => ( + + ), error: getError?.(errors[STEP_ID]), })), [totalErrors, driver] @@ -73,18 +80,19 @@ const Content = ({ isUpdate }) => { /** * Optional configuration about Virtual network. * - * @param {VirtualNetwork} vnet - Virtual network + * @param {VirtualNetwork} data - Virtual network * @returns {object} Optional configuration step */ -const ExtraConfiguration = (vnet) => { - const isUpdate = vnet?.NAME !== undefined +const ExtraConfiguration = ({ data, oneConfig, adminGroup }) => { + const isUpdate = data?.NAME !== undefined return { id: STEP_ID, label: T.AdvancedOptions, - resolver: SCHEMA(isUpdate), + resolver: SCHEMA(isUpdate, oneConfig, adminGroup), optionsValidate: { abortEarly: false }, - content: (formProps) => Content({ ...formProps, isUpdate }), + content: (formProps) => + Content({ ...formProps, isUpdate, oneConfig, adminGroup }), } } @@ -92,6 +100,8 @@ Content.propTypes = { data: PropTypes.any, setFormData: PropTypes.func, isUpdate: PropTypes.bool, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } export default ExtraConfiguration diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/index.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/index.js index 653f4dc593..884b9d8d6c 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/index.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/index.js @@ -14,6 +14,7 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import QoSIcon from 'iconoir-react/dist/DataTransferBoth' +import PropTypes from 'prop-types' import { STEP_ID as EXTRA_ID, @@ -27,9 +28,9 @@ import { import FormWithSchema from 'client/components/Forms/FormWithSchema' import { T } from 'client/constants' -const QoSContent = () => ( +const QoSContent = ({ oneConfig, adminGroup }) => ( <> - {SECTIONS.map(({ id, ...section }) => ( + {SECTIONS(oneConfig, adminGroup).map(({ id, ...section }) => ( ( ) +QoSContent.propTypes = { + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} + /** @type {TabType} */ const TAB = { id: 'qos', name: T.QoS, icon: QoSIcon, Content: QoSContent, - getError: (error) => FIELDS.some(({ name }) => error?.[name]), + getError: (error) => FIELDS().some(({ name }) => error?.[name]), } export default TAB diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/schema.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/schema.js index c9bf6ae2a2..cce0e5df50 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/schema.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/qos/schema.js @@ -15,8 +15,13 @@ * ------------------------------------------------------------------------- */ import { ObjectSchema, string } from 'yup' -import { Field, Section, getObjectSchemaFromFields } from 'client/utils' -import { T, INPUT_TYPES } from 'client/constants' +import { + Field, + Section, + getObjectSchemaFromFields, + disableFields, +} from 'client/utils' +import { T, INPUT_TYPES, RESTRICTED_ATTRIBUTES_TYPE } from 'client/constants' const commonFieldProps = { type: INPUT_TYPES.TEXT, @@ -73,31 +78,39 @@ const OUTBOUND_PEAK_KB_FIELD = { } /** @type {Section[]} Sections */ -const SECTIONS = [ +const SECTIONS = (oneConfig, adminGroup) => [ { id: 'qos-inbound', legend: T.InboundTraffic, - fields: [ - INBOUND_AVG_BW_FIELD, - INBOUND_PEAK_BW_FIELD, - INBOUND_PEAK_KB_FIELD, - ], + fields: disableFields( + [INBOUND_AVG_BW_FIELD, INBOUND_PEAK_BW_FIELD, INBOUND_PEAK_KB_FIELD], + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ), }, { id: 'qos-outbound', legend: T.OutboundTraffic, - fields: [ - OUTBOUND_AVG_BW_FIELD, - OUTBOUND_PEAK_BW_FIELD, - OUTBOUND_PEAK_KB_FIELD, - ], + fields: disableFields( + [OUTBOUND_AVG_BW_FIELD, OUTBOUND_PEAK_BW_FIELD, OUTBOUND_PEAK_KB_FIELD], + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ), }, ] /** @type {Field[]} List of QoS fields */ -const FIELDS = SECTIONS.map(({ fields }) => fields).flat() +const FIELDS = (oneConfig, adminGroup) => + SECTIONS(oneConfig, adminGroup) + .map(({ fields }) => fields) + .flat() /** @type {ObjectSchema} QoS schema */ -const SCHEMA = getObjectSchemaFromFields(FIELDS) +const SCHEMA = (oneConfig, adminGroup) => + getObjectSchemaFromFields(FIELDS(oneConfig, adminGroup)) export { SCHEMA, SECTIONS, FIELDS } diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/schema.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/schema.js index 5ec51ff3d7..c9bb1acfbd 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/schema.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/schema.js @@ -40,12 +40,14 @@ const AR_SCHEMA = object({ /** * @param {boolean} isUpdate - If `true`, the form is being updated + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {ObjectSchema} Extra configuration schema */ -export const SCHEMA = (isUpdate) => { +export const SCHEMA = (isUpdate, oneConfig, adminGroup) => { const schema = object({ SECURITY_GROUPS: array().ensure() }) - .concat(CONTEXT_SCHEMA) - .concat(QOS_SCHEMA) + .concat(CONTEXT_SCHEMA(oneConfig, adminGroup)) + .concat(QOS_SCHEMA(oneConfig, adminGroup)) !isUpdate && schema.concat(AR_SCHEMA) diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/security.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/security.js index 771c2d6d0e..3fde0b1060 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/security.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration/security.js @@ -25,9 +25,13 @@ import { } from 'client/components/Forms/VNetwork/CreateForm/Steps/ExtraConfiguration' import { T } from 'client/constants' +import PropTypes from 'prop-types' + +import { isRestrictedAttributes } from 'client/utils' + export const TAB_ID = 'SECURITY_GROUPS' -const SecurityContent = () => { +const SecurityContent = ({ oneConfig, adminGroup }) => { const TAB_PATH = `${EXTRA_ID}.${TAB_ID}` const { setValue } = useFormContext() @@ -43,6 +47,14 @@ const SecurityContent = () => { setValue(TAB_PATH, newValue) } + const readOnly = + !adminGroup && + isRestrictedAttributes( + 'SECURITY_GROUPS', + undefined, + oneConfig?.VNET_RESTRICTED_ATTR + ) + return ( { pageSize={5} initialState={{ selectedRowIds }} onSelectedRowsChange={handleSelectedRows} + readOnly={readOnly} /> ) } +SecurityContent.propTypes = { + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} + /** @type {TabType} */ const TAB = { id: 'security', diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/index.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/index.js index 03ace4f85a..e8518cdcad 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/index.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/index.js @@ -34,17 +34,21 @@ const DRIVER_PATH = `${STEP_ID}.VN_MAD` const IP_CONF_PATH = `${STEP_ID}.${IP_LINK_CONF_FIELD.name}` /** - * @param {object} props - Props - * @param {boolean} [props.isUpdate] - Is `true` the form will be filter immutable attributes + * @param {boolean} isUpdate - True if it is an update operation + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {ReactElement} Form content component */ -const Content = ({ isUpdate }) => { +const Content = (isUpdate, oneConfig, adminGroup) => { const { setValue } = useFormContext() const driver = useWatch({ name: DRIVER_PATH }) const ipConf = useWatch({ name: IP_CONF_PATH }) || {} - const sections = useMemo(() => SECTIONS(driver, isUpdate), [driver]) + const sections = useMemo( + () => SECTIONS(driver, isUpdate, oneConfig, adminGroup), + [driver] + ) const handleChangeAttribute = (path, newValue) => { const newConf = cloneObject(ipConf) @@ -89,12 +93,12 @@ const Content = ({ isUpdate }) => { /** * General configuration about Virtual network. * - * @param {VirtualNetwork} vnet - Virtual network + * @param {VirtualNetwork} data - Virtual network * @returns {object} General configuration step */ -const General = (vnet) => { - const isUpdate = vnet?.NAME !== undefined - const initialDriver = vnet?.VN_MAD +const General = ({ data, oneConfig, adminGroup }) => { + const isUpdate = data?.NAME !== undefined + const initialDriver = data?.VN_MAD return { id: STEP_ID, @@ -102,7 +106,7 @@ const General = (vnet) => { resolver: (formData) => SCHEMA(formData?.[STEP_ID]?.VN_MAD ?? initialDriver, isUpdate), optionsValidate: { abortEarly: false }, - content: () => Content({ isUpdate }), + content: () => Content(isUpdate, oneConfig, adminGroup), } } diff --git a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/schema.js b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/schema.js index bdeb2ae166..71809b63b0 100644 --- a/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/schema.js +++ b/src/fireedge/src/client/components/Forms/VNetwork/CreateForm/Steps/General/schema.js @@ -22,26 +22,38 @@ import { Section, getObjectSchemaFromFields, filterFieldsByHypervisor, + disableFields, } from 'client/utils' -import { T, VN_DRIVERS } from 'client/constants' +import { T, VN_DRIVERS, RESTRICTED_ATTRIBUTES_TYPE } from 'client/constants' /** * @param {VN_DRIVERS} driver - Virtual network driver * @param {boolean} [isUpdate] - If `true`, the form is being updated + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {Section[]} Fields */ -const SECTIONS = (driver, isUpdate) => [ +const SECTIONS = (driver, isUpdate, oneConfig, adminGroup) => [ { id: 'information', legend: T.Information, - fields: INFORMATION_FIELDS(isUpdate), + fields: disableFields( + INFORMATION_FIELDS(isUpdate), + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET + ), }, { id: 'configuration', legend: T.Configuration, - fields: filterFieldsByHypervisor( - [DRIVER_FIELD, ...FIELDS_BY_DRIVER], - driver + fields: disableFields( + filterFieldsByHypervisor([DRIVER_FIELD, ...FIELDS_BY_DRIVER], driver), + '', + oneConfig, + adminGroup, + RESTRICTED_ATTRIBUTES_TYPE.VNET ), }, ] @@ -49,11 +61,13 @@ const SECTIONS = (driver, isUpdate) => [ /** * @param {VN_DRIVERS} driver - Virtual network driver * @param {boolean} [isUpdate] - If `true`, the form is being updated + * @param {object} oneConfig - Open Nebula configuration + * @param {boolean} adminGroup - If the user belongs to oneadmin group * @returns {BaseSchema} Step schema */ -const SCHEMA = (driver, isUpdate) => +const SCHEMA = (driver, isUpdate, oneConfig, adminGroup) => getObjectSchemaFromFields( - SECTIONS(driver, isUpdate) + SECTIONS(driver, isUpdate, oneConfig, adminGroup) .map(({ schema, fields }) => schema ?? fields) .flat() ).concat(object({ [IP_LINK_CONF_FIELD.name]: IP_LINK_CONF_FIELD.validation })) diff --git a/src/fireedge/src/client/components/Tables/Enhanced/index.js b/src/fireedge/src/client/components/Tables/Enhanced/index.js index de26e44b79..98bf5e784c 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/index.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/index.js @@ -75,8 +75,11 @@ const EnhancedTable = ({ noDataMessage, messages = [], dataDepend, + readOnly = false, }) => { - const styles = EnhancedTableStyles() + const styles = EnhancedTableStyles({ + readOnly: readOnly, + }) const isUninitialized = useMemo( () => isLoading && data === undefined, @@ -258,7 +261,7 @@ const EnhancedTable = ({ refetch={refetch} isLoading={isLoading} singleSelect={singleSelect} - disableRowSelect={disableRowSelect} + disableRowSelect={disableRowSelect || readOnly} globalActions={globalActions} selectedRows={selectedRows} onSelectedRowsChange={onSelectedRowsChange} @@ -294,7 +297,7 @@ const EnhancedTable = ({ {!disableGlobalSort && } {/* SELECTED ROWS */} - {displaySelectedRows && ( + {displaySelectedRows && !readOnly && (
{ typeof onRowClick === 'function' && onRowClick(original) - if (!disableRowSelect) { + if (!disableRowSelect && !readOnly) { if ( singleSelect || (!singleSelect && !(e.ctrlKey || e.metaKey)) @@ -427,6 +430,7 @@ EnhancedTable.propTypes = { ]), messages: PropTypes.array, dataDepend: PropTypes.oneOfType([PropTypes.array, PropTypes.string]), + readOnly: PropTypes.bool, } export * from 'client/components/Tables/Enhanced/Utils' diff --git a/src/fireedge/src/client/components/Tables/Enhanced/styles.js b/src/fireedge/src/client/components/Tables/Enhanced/styles.js index 5c1f422621..0a36e66865 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/styles.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/styles.js @@ -65,7 +65,12 @@ export default makeStyles(({ palette, typography, breakpoints }) => ({ padding: '0.8em', cursor: 'pointer', color: palette.text.primary, - backgroundColor: palette.background.paper, + /** + * @param {object} props - Properties of the styles + * @returns {object} - Background color + */ + backgroundColor: (props) => + props.readOnly ? palette.action.hover : palette.background.paper, fontWeight: typography.fontWeightRegular, fontSize: '1em', border: `1px solid ${palette.divider}`, diff --git a/src/fireedge/src/client/components/Tabs/VNetwork/Address.js b/src/fireedge/src/client/components/Tabs/VNetwork/Address.js index 65d2ea6b23..dcff10d79f 100644 --- a/src/fireedge/src/client/components/Tabs/VNetwork/Address.js +++ b/src/fireedge/src/client/components/Tabs/VNetwork/Address.js @@ -37,9 +37,16 @@ const { ADD_AR, UPDATE_AR, DELETE_AR } = VN_ACTIONS * @param {object} props.tabProps - Tab information * @param {string[]} props.tabProps.actions - Actions tab * @param {string} props.id - Virtual Network id + * @param {object} props.oneConfig - Open Nebula configuration + * @param {boolean} props.adminGroup - If the user belongs to oneadmin group * @returns {ReactElement} AR tab */ -const AddressTab = ({ tabProps: { actions } = {}, id }) => { +const AddressTab = ({ + tabProps: { actions } = {}, + id, + oneConfig, + adminGroup, +}) => { const { data: vnet } = useGetVNetworkQuery({ id }) /** @type {AddressRange[]} */ @@ -47,7 +54,13 @@ const AddressTab = ({ tabProps: { actions } = {}, id }) => { return ( - {actions[ADD_AR] === true && } + {actions[ADD_AR] === true && ( + + )} {addressRanges.map((ar) => ( @@ -58,10 +71,20 @@ const AddressTab = ({ tabProps: { actions } = {}, id }) => { actions={ <> {actions[UPDATE_AR] === true && ( - + )} {actions[DELETE_AR] === true && ( - + )} } @@ -75,6 +98,8 @@ const AddressTab = ({ tabProps: { actions } = {}, id }) => { AddressTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } AddressTab.displayName = 'AddressTab' diff --git a/src/fireedge/src/client/components/Tabs/VNetwork/Security.js b/src/fireedge/src/client/components/Tabs/VNetwork/Security.js index f472626255..6130e2009c 100644 --- a/src/fireedge/src/client/components/Tabs/VNetwork/Security.js +++ b/src/fireedge/src/client/components/Tabs/VNetwork/Security.js @@ -30,6 +30,8 @@ import { SecurityGroupsTable, GlobalAction } from 'client/components/Tables' import { T, VN_ACTIONS, RESOURCE_NAMES } from 'client/constants' import { PATH } from 'client/apps/sunstone/routesOne' +import { isRestrictedAttributes } from 'client/utils' + const { SEC_GROUP } = RESOURCE_NAMES const { ADD_SECGROUP } = VN_ACTIONS @@ -40,9 +42,16 @@ const { ADD_SECGROUP } = VN_ACTIONS * @param {object} props.tabProps - Tab information * @param {string[]} props.tabProps.actions - Actions tab * @param {string} props.id - Virtual Network id + * @param {object} props.oneConfig - OpenNebula configuration + * @param {boolean} props.adminGroup - If the user belongs to the oneadmin group * @returns {ReactElement} Security Groups tab */ -const SecurityTab = ({ tabProps: { actions } = {}, id }) => { +const SecurityTab = ({ + tabProps: { actions } = {}, + id, + oneConfig, + adminGroup, +}) => { const { push: redirectTo } = useHistory() const { data: vnet } = useGetVNetworkQuery({ id }) @@ -65,23 +74,31 @@ const SecurityTab = ({ tabProps: { actions } = {}, id }) => { }) /** @type {GlobalAction[]} */ - const globalActions = [ - actions[ADD_SECGROUP] && { - accessor: VN_ACTIONS.ADD_SECGROUP, - dataCy: VN_ACTIONS.ADD_SECGROUP, - tooltip: T.SecurityGroup, - icon: AddIcon, - options: [ - { - dialogProps: { title: T.SecurityGroup }, - form: undefined, - onSubmit: () => async (formData) => { - console.log({ formData }) + const globalActions = + adminGroup || + !isRestrictedAttributes( + 'SECURITY_GROUPS', + undefined, + oneConfig?.VNET_RESTRICTED_ATTR + ) + ? [ + actions[ADD_SECGROUP] && { + accessor: VN_ACTIONS.ADD_SECGROUP, + dataCy: VN_ACTIONS.ADD_SECGROUP, + tooltip: T.SecurityGroup, + icon: AddIcon, + options: [ + { + dialogProps: { title: T.SecurityGroup }, + form: undefined, + onSubmit: () => async (formData) => { + console.log({ formData }) + }, + }, + ], }, - }, - ], - }, - ].filter(Boolean) + ].filter(Boolean) + : undefined return ( @@ -100,6 +117,8 @@ const SecurityTab = ({ tabProps: { actions } = {}, id }) => { SecurityTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } SecurityTab.displayName = 'SecurityTab' diff --git a/src/fireedge/src/client/components/Tabs/VNetwork/index.js b/src/fireedge/src/client/components/Tabs/VNetwork/index.js index 35297dbe34..ef74b47561 100644 --- a/src/fireedge/src/client/components/Tabs/VNetwork/index.js +++ b/src/fireedge/src/client/components/Tabs/VNetwork/index.js @@ -18,7 +18,7 @@ import PropTypes from 'prop-types' import { memo, useMemo } from 'react' import { RESOURCE_NAMES } from 'client/constants' -import { useViews } from 'client/features/Auth' +import { useViews, useSystemData } from 'client/features/Auth' import { useGetVNetworkQuery } from 'client/features/OneApi/network' import { getAvailableInfoTabs } from 'client/models/Helper' @@ -46,11 +46,19 @@ const VNetworkTabs = memo(({ id }) => { id, }) + const { adminGroup, oneConfig } = useSystemData() + const tabsAvailable = useMemo(() => { const resource = RESOURCE_NAMES.VNET const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {} - return getAvailableInfoTabs(infoTabs, getTabComponent, id) + return getAvailableInfoTabs( + infoTabs, + getTabComponent, + id, + oneConfig, + adminGroup + ) }, [view, id]) if (isError) { diff --git a/src/fireedge/src/client/constants/system.js b/src/fireedge/src/client/constants/system.js index f0175c063f..88a5290e49 100644 --- a/src/fireedge/src/client/constants/system.js +++ b/src/fireedge/src/client/constants/system.js @@ -15,3 +15,9 @@ * ------------------------------------------------------------------------- */ /** @enum {string} Base path for Open Nebula documentation */ export const DOCS_BASE_PATH = 'https://docs.opennebula.io' + +export const RESTRICTED_ATTRIBUTES_TYPE = { + VM: 'VM_RESTRICTED_ATTR', + IMAGE: 'IMAGE_RESTRICTED_ATTR', + VNET: 'VNET_RESTRICTED_ATTR', +} diff --git a/src/fireedge/src/client/containers/Images/Create.js b/src/fireedge/src/client/containers/Images/Create.js index 3eb9f007c4..04d7e7e5f4 100644 --- a/src/fireedge/src/client/containers/Images/Create.js +++ b/src/fireedge/src/client/containers/Images/Create.js @@ -31,6 +31,10 @@ import { import { CreateForm } from 'client/components/Forms/Image' import { PATH } from 'client/apps/sunstone/routesOne' +import { useSystemData } from 'client/features/Auth' + +const _ = require('lodash') + /** * Displays the creation or modification form to a VM Template. * @@ -41,6 +45,7 @@ function CreateImage() { const [allocate] = useAllocateImageMutation() const [upload] = useUploadImageMutation() const { enqueueSuccess, uploadSnackbar } = useGeneralApi() + const { adminGroup, oneConfig } = useSystemData() useGetDatastoresQuery(undefined, { refetchOnMountOrArgChange: false }) const onSubmit = async ({ template, datastore, file }) => { @@ -71,10 +76,19 @@ function CreateImage() { } catch {} } - return ( - }> + return !_.isEmpty(oneConfig) ? ( + } + > {(config) => } + ) : ( + ) } diff --git a/src/fireedge/src/client/containers/VirtualNetworks/Create.js b/src/fireedge/src/client/containers/VirtualNetworks/Create.js index b7a3830d3e..3f32d4d3e8 100644 --- a/src/fireedge/src/client/containers/VirtualNetworks/Create.js +++ b/src/fireedge/src/client/containers/VirtualNetworks/Create.js @@ -30,6 +30,10 @@ import { import { CreateForm } from 'client/components/Forms/VNetwork' import { PATH } from 'client/apps/sunstone/routesOne' +import { useSystemData } from 'client/features/Auth' + +const _ = require('lodash') + /** * Displays the creation or modification form to a Virtual Network. * @@ -42,6 +46,7 @@ function CreateVirtualNetwork() { const { enqueueSuccess } = useGeneralApi() const [update] = useUpdateVNetMutation() const [allocate] = useAllocateVnetMutation() + const { adminGroup, oneConfig } = useSystemData() const { data } = useGetVNetworkQuery( { id: vnetId, extended: true }, @@ -62,17 +67,21 @@ function CreateVirtualNetwork() { } catch {} } - return vnetId && !data ? ( - - ) : ( + return !_.isEmpty(oneConfig) && ((vnetId && data) || !vnetId) ? ( } > {(config) => } + ) : ( + ) } diff --git a/src/fireedge/src/client/utils/restrictedAttributes.js b/src/fireedge/src/client/utils/restrictedAttributes.js index f00baa311a..449d2517ec 100644 --- a/src/fireedge/src/client/utils/restrictedAttributes.js +++ b/src/fireedge/src/client/utils/restrictedAttributes.js @@ -328,3 +328,28 @@ export const hasRestrictedAttributes = ( return !!restricteAttribute } + +/** + * Find if an attribute is a restricted attribute. + * + * @param {object} attribute - The attribute + * @param {string} section - Section of the attribute + * @param {Array} restrictedAttributes - List of restricted attributes + * @returns {boolean} - True if it is restricted attribute + */ +export const isRestrictedAttributes = ( + attribute, + section = 'PARENT', + restrictedAttributes = [] +) => { + // Create map with restricted attributes + const mapRestrictedAttributes = + mapRestrictedAttributesFunction(restrictedAttributes) + + // Find if there is a restricted attribute in the item + const restricteAttribute = mapRestrictedAttributes[section]?.find( + (restAttr) => restAttr === attribute + ) + + return !!restricteAttribute +} diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js index d7511d566b..cad537c3ab 100644 --- a/src/fireedge/src/client/utils/schema.js +++ b/src/fireedge/src/client/utils/schema.js @@ -41,6 +41,7 @@ import { VN_DRIVERS, INPUT_TYPES, USER_INPUT_TYPES, + RESTRICTED_ATTRIBUTES_TYPE, } from 'client/constants' import { stringToBoolean } from 'client/models/Helper' @@ -505,13 +506,19 @@ export const createForm = fields(props), props.nameParentAttribute, props.oneConfig, - props.adminGroup + props.adminGroup, + props && props.restrictedAttributesType + ? props.restrictedAttributesType + : RESTRICTED_ATTRIBUTES_TYPE.VM ) : disableFields( fields, props.nameParentAttribute, props.oneConfig, - props.adminGroup + props.adminGroup, + props && props.restrictedAttributesType + ? props.restrictedAttributesType + : RESTRICTED_ATTRIBUTES_TYPE.VM ) : typeof fields === 'function' ? fields(props) @@ -555,23 +562,28 @@ export const createForm = * @param {string} nameParentAttribute - Parent name of the form * @param {object} oneConfig - Config of oned.conf * @param {boolean} adminGroup - It he user is an admin + * @param {string} type - The type of restricted attributes use to filter * @returns {Array} - New array of fields */ export const disableFields = ( - fields = {}, + fields = [], nameParentAttribute, oneConfig = {}, - adminGroup = true + adminGroup = true, + type = RESTRICTED_ATTRIBUTES_TYPE.VM ) => { // Disable fields only if it is a non admin user if (adminGroup) return fields // Get restricted attributes - const restrictedAttributes = oneConfig?.VM_RESTRICTED_ATTR?.filter((item) => - nameParentAttribute !== '' - ? item.startsWith(nameParentAttribute) - : !item.includes('/') - ).map((item) => item.split('/')[1] ?? item) + const listRestrictedAttributes = oneConfig[type] + const restrictedAttributes = listRestrictedAttributes + .filter((item) => + nameParentAttribute !== '' + ? item.startsWith(nameParentAttribute) + : !item.includes('/') + ) + .map((item) => item.split('/')[1] ?? item) // Iterate over each field and add disabled attribute if it's a restricted attribute (almost all forms has attributes with name like "ATTR" but some of them like "PARENT.ATTR") return fields.map((field) => { diff --git a/src/fireedge/src/server/routes/api/system/functions.js b/src/fireedge/src/server/routes/api/system/functions.js index 8155c347d3..ed6a47923b 100644 --- a/src/fireedge/src/server/routes/api/system/functions.js +++ b/src/fireedge/src/server/routes/api/system/functions.js @@ -37,6 +37,8 @@ const ALLOWED_KEYS_ONED_CONF = [ 'AUTH_MAD', 'FEDERATION', 'VM_RESTRICTED_ATTR', + 'IMAGE_RESTRICTED_ATTR', + 'VNET_RESTRICTED_ATTR', ] /**