From d7669a7cc1e640c3b723553253cd1aaa0ff5c587 Mon Sep 17 00:00:00 2001 From: nevragurses Date: Wed, 8 May 2024 11:04:46 +0000 Subject: [PATCH] feat: The RBAC Policies has been added. Change-Id: I9b35371595d2aefb77ad2122e6c6bfec6d89bb41 --- ..._policies_to_neutron-1578109a8642e3a3.yaml | 6 + src/client/neutron/index.js | 5 + src/layouts/admin-menu.jsx | 15 ++ .../containers/RbacPolicies/Detail/Detail.jsx | 57 +++++ .../containers/RbacPolicies/Detail/index.jsx | 48 +++++ .../RbacPolicies/actions/Create.jsx | 201 ++++++++++++++++++ .../RbacPolicies/actions/Delete.jsx | 38 ++++ .../containers/RbacPolicies/actions/Edit.jsx | 78 +++++++ .../containers/RbacPolicies/actions/index.jsx | 30 +++ .../network/containers/RbacPolicies/index.jsx | 98 +++++++++ src/pages/network/routes/index.js | 12 ++ src/stores/index.jsx | 2 + src/stores/neutron/rbac-policies.js | 76 +++++++ 13 files changed, 666 insertions(+) create mode 100644 releasenotes/notes/feat_rbac_policies_to_neutron-1578109a8642e3a3.yaml create mode 100644 src/pages/network/containers/RbacPolicies/Detail/Detail.jsx create mode 100644 src/pages/network/containers/RbacPolicies/Detail/index.jsx create mode 100644 src/pages/network/containers/RbacPolicies/actions/Create.jsx create mode 100644 src/pages/network/containers/RbacPolicies/actions/Delete.jsx create mode 100644 src/pages/network/containers/RbacPolicies/actions/Edit.jsx create mode 100644 src/pages/network/containers/RbacPolicies/actions/index.jsx create mode 100644 src/pages/network/containers/RbacPolicies/index.jsx create mode 100644 src/stores/neutron/rbac-policies.js diff --git a/releasenotes/notes/feat_rbac_policies_to_neutron-1578109a8642e3a3.yaml b/releasenotes/notes/feat_rbac_policies_to_neutron-1578109a8642e3a3.yaml new file mode 100644 index 00000000..dcae6503 --- /dev/null +++ b/releasenotes/notes/feat_rbac_policies_to_neutron-1578109a8642e3a3.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The RBAC Policies has been added to Networks. + + RBAC Policies and its actions have been included under the Networks in administrator page. \ No newline at end of file diff --git a/src/client/neutron/index.js b/src/client/neutron/index.js index 43c7b91f..9717dc57 100644 --- a/src/client/neutron/index.js +++ b/src/client/neutron/index.js @@ -97,6 +97,11 @@ export class NeutronClient extends Base { }, ], }, + { + name: 'rbacPolicies', + key: 'rbac-policies', + responseKey: 'rbac_policy', + }, { name: 'firewalls', key: 'fwaas/firewall_groups', diff --git a/src/layouts/admin-menu.jsx b/src/layouts/admin-menu.jsx index 26b12154..e69bb6b1 100644 --- a/src/layouts/admin-menu.jsx +++ b/src/layouts/admin-menu.jsx @@ -447,6 +447,21 @@ const renderMenu = (t) => { }, ], }, + { + path: '/network/rbac-policies-admin', + name: t('RBAC Policies'), + key: 'rbacPolicyAdmin', + level: 1, + children: [ + { + path: /^\/network\/rbac-policies-admin\/detail\/.[^/]+$/, + name: t('RBAC Policy Detail'), + key: 'rbacPolicyDetailAdmin', + level: 2, + routePath: '/network/rbac-policies-admin/detail/:id', + }, + ], + }, { path: '/network/firewall-admin', name: t('Firewalls'), diff --git a/src/pages/network/containers/RbacPolicies/Detail/Detail.jsx b/src/pages/network/containers/RbacPolicies/Detail/Detail.jsx new file mode 100644 index 00000000..86b49548 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/Detail/Detail.jsx @@ -0,0 +1,57 @@ +// 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 { inject, observer } from 'mobx-react'; +import Base from 'containers/BaseDetail'; + +export class BaseDetail extends Base { + get leftCards() { + const cards = [this.baseInfoCard]; + return cards; + } + + get baseInfoCard() { + const options = [ + { + label: t('ID'), + dataIndex: 'id', + }, + { + label: t('Project ID'), + dataIndex: 'project_id', + }, + { + label: t('Object Type'), + dataIndex: 'object_type', + }, + { + label: t('Object ID'), + dataIndex: 'object_id', + }, + { + label: t('Action'), + dataIndex: 'action', + }, + { + label: t('Target Tenant'), + dataIndex: 'target_tenant', + }, + ]; + + return { + title: t('Rbac Policy Detail'), + options, + }; + } +} + +export default inject('rootStore')(observer(BaseDetail)); diff --git a/src/pages/network/containers/RbacPolicies/Detail/index.jsx b/src/pages/network/containers/RbacPolicies/Detail/index.jsx new file mode 100644 index 00000000..3f41b385 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/Detail/index.jsx @@ -0,0 +1,48 @@ +// 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 { inject, observer } from 'mobx-react'; +import Base from 'containers/TabDetail'; +import actionConfigs from 'src/pages/network/containers/RbacPolicies/actions'; +import { RbacPoliciesStore } from 'src/stores/neutron/rbac-policies'; +import Detail from './Detail'; + +export class RbacPolicyDetail extends Base { + get name() { + return t('Rbac Policy'); + } + + get listUrl() { + return this.getRoutePath('rbacPoliciesAdmin'); + } + + get actionConfigs() { + return actionConfigs; + } + + get tabs() { + const tabs = [ + { + title: t('Detail'), + key: 'rbacPolicyDetailAdmin', + component: Detail, + }, + ]; + return tabs; + } + + init() { + this.store = new RbacPoliciesStore(); + } +} + +export default inject('rootStore')(observer(RbacPolicyDetail)); diff --git a/src/pages/network/containers/RbacPolicies/actions/Create.jsx b/src/pages/network/containers/RbacPolicies/actions/Create.jsx new file mode 100644 index 00000000..99dd64f9 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/actions/Create.jsx @@ -0,0 +1,201 @@ +// 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 { inject, observer } from 'mobx-react'; +import { ModalAction } from 'containers/Action'; +import Notify from 'src/components/Notify'; +import { RbacPoliciesStore } from 'src/stores/neutron/rbac-policies'; +import { ProjectStore } from 'stores/keystone/project'; +import { NetworkStore } from 'stores/neutron/network'; +import { QoSPolicyStore } from 'stores/neutron/qos-policy'; +import { observable } from 'mobx'; + +export class Create extends ModalAction { + static id = 'create-policy'; + + static title = t('Create'); + + @observable allNetworks; + + @observable allProjects; + + get name() { + return t('Create'); + } + + init() { + this.state = { + ...this.state, + isReady: false, + }; + this.store = new RbacPoliciesStore(); + this.projectStore = new ProjectStore(); + this.qosPolicyStore = new QoSPolicyStore(); + this.networkStore = new NetworkStore(); + this.getAllData(); + } + + async getAllData() { + await Promise.all([ + this.getProjects(), + this.getQoSPolicy(), + this.getNetworks(), + ]); + this.setState({ isReady: true }); + } + + get tips() { + return t('From here you can create a rbac policy.'); + } + + async getProjects() { + this.allProjects = await this.projectStore.pureFetchList(); + this.addNewElementToProjectList(); + } + + addNewElementToProjectList() { + const newElement = { + id: '*', + name: '*', + }; + this.allProjects.unshift(newElement); + } + + async getQoSPolicy() { + await this.qosPolicyStore.fetchList(); + } + + async getNetworks() { + this.allNetworks = await this.networkStore.pureFetchList(); + } + + get projects() { + return (this.allProjects || []).map((it) => ({ + value: it.id, + label: it.name, + })); + } + + get qosPolicy() { + return (this.qosPolicyStore.list.data || []).map((it) => ({ + value: it.id, + label: it.name, + })); + } + + get networks() { + return (this.allNetworks || []).map((it) => ({ + value: it.id, + label: it.name, + })); + } + + onSubmit = async (values) => { + try { + const { object_type, ...rest } = values; + const action = + object_type === 'network' || object_type === 'qos_policy' + ? 'access_as_shared' + : 'access_as_external'; + const updatedType = + object_type === 'external-network' ? 'network' : object_type; + const body = { + ...rest, + object_type: updatedType, + action, + }; + + await this.store.create(body); + } catch (error) { + Notify.errorWithDetail(null, error.toString()); + return Promise.reject(error); + } + }; + + static allowed = () => Promise.resolve(true); + + get createObjectList() { + return [ + { value: 'network', label: t('Shared Network') }, + { value: 'external-network', label: t('External Network') }, + { value: 'qos_policy', label: t('Shared QoS Policy') }, + ]; + } + + onChangeHandler = async (value) => { + this.setState({ + object_type: value, + }); + }; + + get formItems() { + const { object_type } = this.state; + + const isNetwork = object_type === 'network'; + const isExternalNetwork = object_type === 'external-network'; + const isQosPolicy = object_type === 'qos_policy'; + + return [ + { + name: 'target_tenant', + label: t('Target Project'), + placeholder: t('Select a project'), + type: 'select', + options: this.projects, + isLoading: !this.state.isReady, + required: true, + }, + { + name: 'object_type', + label: t('Action and Object Type'), + placeholder: t('Select action and object type'), + type: 'select', + onChange: this.onChangeHandler, + options: this.createObjectList, + required: true, + }, + { + name: 'object_id', + label: t('Shared Network'), + placeholder: t('Select a network'), + type: 'select', + options: this.networks, + hidden: !isNetwork, + isLoading: !this.state.isReady, + onChange: this.onSourceEnvironmentChange, + required: true, + }, + { + name: 'object_id', + label: t('External Network'), + placeholder: t('Select a network'), + type: 'select', + options: this.networks, + hidden: !isExternalNetwork, + isLoading: !this.state.isReady, + required: true, + }, + { + name: 'object_id', + label: t('QoS Policy'), + placeholder: t('Select a QoS Policy'), + type: 'select', + options: this.qosPolicy, + hidden: !isQosPolicy, + isLoading: !this.state.isReady, + required: true, + }, + ]; + } +} + +export default inject('rootStore')(observer(Create)); diff --git a/src/pages/network/containers/RbacPolicies/actions/Delete.jsx b/src/pages/network/containers/RbacPolicies/actions/Delete.jsx new file mode 100644 index 00000000..c6029f7f --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/actions/Delete.jsx @@ -0,0 +1,38 @@ +// 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 { ConfirmAction } from 'containers/Action'; +import globalRbacPoliciesStore from 'stores/neutron/rbac-policies'; + +export default class Delete extends ConfirmAction { + get id() { + return 'delete'; + } + + get title() { + return t('Delete RBAC Policy'); + } + + get isDanger() { + return true; + } + + get buttonText() { + return t('Delete'); + } + + get actionName() { + return t('delete'); + } + + onSubmit = (data) => globalRbacPoliciesStore.delete(data); +} diff --git a/src/pages/network/containers/RbacPolicies/actions/Edit.jsx b/src/pages/network/containers/RbacPolicies/actions/Edit.jsx new file mode 100644 index 00000000..7c383680 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/actions/Edit.jsx @@ -0,0 +1,78 @@ +// 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 { inject, observer } from 'mobx-react'; +import { ModalAction } from 'containers/Action'; +import Notify from 'src/components/Notify'; +import { RbacPoliciesStore } from 'src/stores/neutron/rbac-policies'; +import { ProjectStore } from 'stores/keystone/project'; + +export class Edit extends ModalAction { + static id = 'edit-policy'; + + static title = t('Edit'); + + get name() { + return t('Edit'); + } + + init() { + this.store = new RbacPoliciesStore(); + this.projectStore = new ProjectStore(); + this.getProjects(); + } + + get tips() { + return t('You may update the editable properties of the RBAC policy here.'); + } + + async getProjects() { + await this.projectStore.fetchProjectsWithDomain(); + this.setState({ ...this.state, isReady: true }); + } + + get projects() { + return (this.projectStore.list.data || []).map((it) => ({ + value: it.id, + label: it.name, + })); + } + + onSubmit = async (values) => { + const { id } = this.item; + try { + const { ...body } = values; + await this.store.update(id, body); + } catch (error) { + Notify.errorWithDetail(null, error.toString()); + return Promise.reject(error); + } + }; + + static allowed = () => Promise.resolve(true); + + get formItems() { + return [ + { + name: 'target_tenant', + label: t('Target Project'), + placeholder: t('Select a project'), + type: 'select', + options: this.projects, + isLoading: this.projectStore.list.isLoading, + required: true, + }, + ]; + } +} + +export default inject('rootStore')(observer(Edit)); diff --git a/src/pages/network/containers/RbacPolicies/actions/index.jsx b/src/pages/network/containers/RbacPolicies/actions/index.jsx new file mode 100644 index 00000000..9bb97e27 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/actions/index.jsx @@ -0,0 +1,30 @@ +// 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 Delete from './Delete'; +import Edit from './Edit'; +import Create from './Create'; + +const actionConfigs = { + rowActions: { + firstAction: Edit, + moreActions: [ + { + action: Delete, + }, + ], + }, + batchActions: [Delete], + primaryActions: [Create], +}; + +export default actionConfigs; diff --git a/src/pages/network/containers/RbacPolicies/index.jsx b/src/pages/network/containers/RbacPolicies/index.jsx new file mode 100644 index 00000000..4addb0c5 --- /dev/null +++ b/src/pages/network/containers/RbacPolicies/index.jsx @@ -0,0 +1,98 @@ +// 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 { observer, inject } from 'mobx-react'; +import Base from 'containers/List'; +import { RbacPoliciesStore } from 'src/stores/neutron/rbac-policies'; +import actionConfigRbacPolicies from './actions'; + +export class RbacPolicies extends Base { + init() { + this.store = new RbacPoliciesStore(); + this.downloadStore = new RbacPoliciesStore(); + } + + get hasTab() { + return false; + } + + get name() { + return t('RBAC Policies'); + } + + get actionConfigs() { + return actionConfigRbacPolicies; + } + + getColumns = () => { + const columns = [ + { + title: t('Project'), + dataIndex: 'project_name', + }, + { + title: t('ID'), + dataIndex: 'id', + routeName: this.getRouteName('rbacPolicyDetail'), + isLink: true, + }, + { + title: t('Object Type'), + dataIndex: 'object_type', + }, + { + title: t('Object'), + dataIndex: 'object_name', + }, + { + title: t('Target Project'), + dataIndex: 'target_tenant_name', + }, + ]; + return columns; + }; + + get objectTypes() { + return [ + { key: 'network', label: t('Network') }, + { key: 'qos_policy', label: t('QoS Policy') }, + ]; + } + + get searchFilters() { + return [ + { + label: t('Project'), + name: 'project_name', + }, + { + label: t('Id'), + name: 'id', + }, + { + label: t('Target Project'), + name: 'target_tenant_name', + }, + { + label: t('Object Type'), + name: 'object_type', + options: this.objectTypes, + }, + { + label: t('Object'), + name: 'object_name', + }, + ]; + } +} + +export default inject('rootStore')(observer(RbacPolicies)); diff --git a/src/pages/network/routes/index.js b/src/pages/network/routes/index.js index 5a0eb70f..2716c12c 100644 --- a/src/pages/network/routes/index.js +++ b/src/pages/network/routes/index.js @@ -55,6 +55,8 @@ import RuleDetail from '../containers/Firewall/Rule/Detail'; import PolicyCreate from '../containers/Firewall/Policy/actions/Create'; import PolicyEdit from '../containers/Firewall/Policy/actions/Edit'; import FirewallCreate from '../containers/Firewall/Firewall/actions/Create'; +import RbacPolicies from '../containers/RbacPolicies'; +import RbacPolicyDetail from '../containers/RbacPolicies/Detail'; const PATH = '/network'; export default [ @@ -354,6 +356,16 @@ export default [ component: PolicyEdit, exact: true, }, + { + path: `${PATH}/rbac-policies-admin`, + component: RbacPolicies, + exact: true, + }, + { + path: `${PATH}/rbac-policies-admin/detail/:id`, + component: RbacPolicyDetail, + exact: true, + }, { path: '*', component: E404 }, ], }, diff --git a/src/stores/index.jsx b/src/stores/index.jsx index f83a2d8d..e00a3a7e 100644 --- a/src/stores/index.jsx +++ b/src/stores/index.jsx @@ -17,6 +17,7 @@ import globalVolumeStore from 'stores/cinder/volume'; import globalComputeHostStore from 'stores/nova/compute-host'; import globalHypervisorStore from 'stores/nova/hypervisor'; import globalStackStore from 'stores/heat/stack'; +import globalRbacPoliciesStore from 'stores/neutron/rbac-policies'; export default { globalFloatingIpsStore, @@ -38,4 +39,5 @@ export default { globalHypervisorStore, globalStackStore, globalPortStore, + globalRbacPoliciesStore, }; diff --git a/src/stores/neutron/rbac-policies.js b/src/stores/neutron/rbac-policies.js new file mode 100644 index 00000000..52ceeece --- /dev/null +++ b/src/stores/neutron/rbac-policies.js @@ -0,0 +1,76 @@ +import Base from 'stores/base'; +import client from 'client'; + +export class RbacPoliciesStore extends Base { + get client() { + return client.neutron.rbacPolicies; + } + + get listFilterByProject() { + return true; + } + + get listResponseKey() { + return 'rbac_policies'; + } + + get projectClient() { + return client.keystone.projects; + } + + get qosClient() { + return client.neutron.qosPolicies; + } + + get networkClient() { + return client.neutron.networks; + } + + async listDidFetch(items) { + const [ + { networks: allNetworks }, + { policies: allPolicies }, + { projects: allProjects }, + ] = await Promise.all([ + this.networkClient.list(), + this.qosClient.list(), + this.projectClient.list(), + ]); + const updatedItems = items.map((item) => { + const networkOfItem = allNetworks.find( + (network) => network.id === item.object_id + ); + const policyOfItem = allPolicies.find( + (policy) => policy.id === item.object_id + ); + const targetTenant = allProjects.find( + (project) => project.id === item.target_tenant + ); + return { + ...item, + object_name: networkOfItem + ? networkOfItem.name + : policyOfItem + ? policyOfItem.name + : '-', + target_tenant_name: targetTenant ? targetTenant.name : '*', + }; + }); + return updatedItems; + } + + async getProjects() { + await this.projectClient.list(); + } + + async getQoSPolicy() { + await this.qosPolicyClient.list(); + } + + async getNetworks() { + await this.networkClient.list(); + } +} + +const globalRbacPoliciesStore = new RbacPoliciesStore(); +export default globalRbacPoliciesStore;