diff --git a/src/pages/network/containers/Certificate/Certificate.jsx b/src/pages/network/containers/Certificate/Certificate.jsx index 363a75f2..d77c4305 100644 --- a/src/pages/network/containers/Certificate/Certificate.jsx +++ b/src/pages/network/containers/Certificate/Certificate.jsx @@ -94,11 +94,29 @@ export class Certificate extends Base { }, { title: t('Domain Name'), - dataIndex: 'algorithm', + dataIndex: 'domain', render: (value) => value || '-', hidden: this.currentMode === 'CA', isHideable: true, }, + { + title: t('Listener'), + dataIndex: 'listener', + render: (value) => { + return value + ? this.getLinkRender( + 'lbListenerDetail', + value.name, + { + loadBalancerId: value.lb, + id: value.id, + }, + null + ) + : '-'; + }, + isHideable: true, + }, { title: t('Status'), dataIndex: 'status', diff --git a/src/pages/network/containers/Certificate/Detail/Container/index.jsx b/src/pages/network/containers/Certificate/Detail/Container/index.jsx index 201e60c9..f940cbd4 100644 --- a/src/pages/network/containers/Certificate/Detail/Container/index.jsx +++ b/src/pages/network/containers/Certificate/Detail/Container/index.jsx @@ -15,7 +15,7 @@ import { inject, observer } from 'mobx-react'; import Base from 'containers/TabDetail'; import { ContainersStore } from 'stores/barbican/containers'; -import { certificateColumns } from 'resources/octavia/lb'; +import { getCertificateColumns } from 'resources/octavia/lb'; import BaseDetail from './BaseDetail'; import actionConfigs from '../../actions'; @@ -41,7 +41,7 @@ export class Detail extends Base { } get detailInfos() { - return certificateColumns; + return getCertificateColumns(this); } get tabs() { diff --git a/src/pages/network/containers/Certificate/Detail/Secret/index.jsx b/src/pages/network/containers/Certificate/Detail/Secret/index.jsx index ad088795..a86f2cc5 100644 --- a/src/pages/network/containers/Certificate/Detail/Secret/index.jsx +++ b/src/pages/network/containers/Certificate/Detail/Secret/index.jsx @@ -15,7 +15,7 @@ import { inject, observer } from 'mobx-react'; import Base from 'containers/TabDetail'; import { SecretsStore } from 'stores/barbican/secrets'; -import { certificateColumns } from 'resources/octavia/lb'; +import { getCertificateColumns } from 'resources/octavia/lb'; import BaseDetail from './BaseDetail'; import actionConfigs from '../../actions'; @@ -41,7 +41,9 @@ export class Detail extends Base { } get detailInfos() { - return certificateColumns.filter((it) => it.dataIndex !== 'algorithm'); + return getCertificateColumns(this).filter( + (it) => it.dataIndex !== 'domain' + ); } get tabs() { diff --git a/src/pages/network/containers/Certificate/actions/DeleteContainer.jsx b/src/pages/network/containers/Certificate/actions/DeleteContainer.jsx index 96af1190..271e6e01 100644 --- a/src/pages/network/containers/Certificate/actions/DeleteContainer.jsx +++ b/src/pages/network/containers/Certificate/actions/DeleteContainer.jsx @@ -38,7 +38,7 @@ export default class DeleteAction extends ConfirmAction { policy = ['barbican:secret:delete', 'barbican:container:delete']; - allowedCheckFunc = () => true; + allowedCheckFunc = (item) => !item.listener; onSubmit = (data) => { return globalContainersStore.delete(data); diff --git a/src/pages/network/containers/Certificate/actions/DeleteSecret.jsx b/src/pages/network/containers/Certificate/actions/DeleteSecret.jsx index ff62f816..bcaa8c29 100644 --- a/src/pages/network/containers/Certificate/actions/DeleteSecret.jsx +++ b/src/pages/network/containers/Certificate/actions/DeleteSecret.jsx @@ -38,7 +38,7 @@ export default class DeleteAction extends ConfirmAction { policy = 'barbican:secret:delete'; - allowedCheckFunc = () => true; + allowedCheckFunc = (item) => !item.listener; onSubmit = (data) => { return globalSecretsStore.delete(data); diff --git a/src/pages/network/containers/LoadBalancers/Listener/Actions/CreateListener.jsx b/src/pages/network/containers/LoadBalancers/Listener/Actions/CreateListener.jsx index 5e0fe6fd..352cc65d 100644 --- a/src/pages/network/containers/LoadBalancers/Listener/Actions/CreateListener.jsx +++ b/src/pages/network/containers/LoadBalancers/Listener/Actions/CreateListener.jsx @@ -18,7 +18,7 @@ import { ListenerStore } from 'stores/octavia/listener'; import { ContainersStore } from 'stores/barbican/containers'; import { SecretsStore } from 'stores/barbican/secrets'; import { - certificateColumns, + getCertificateColumns, listenerProtocols, sslParseMethod, } from 'resources/octavia/lb'; @@ -74,9 +74,7 @@ export class Create extends ModalAction { } get SNICertificate() { - return (this.containersStore.list.data || []).filter( - (it) => !!it.algorithm - ); + return (this.containersStore.list.data || []).filter((it) => !!it.domain); } get isEdit() { @@ -140,7 +138,7 @@ export class Create extends ModalAction { name: 'name', }, ], - columns: certificateColumns, + columns: getCertificateColumns(this), display: protocol === 'TERMINATED_HTTPS', }, { @@ -157,8 +155,8 @@ export class Create extends ModalAction { name: 'name', }, ], - columns: certificateColumns.filter( - (it) => it.dataIndex !== 'algorithm' + columns: getCertificateColumns(this).filter( + (it) => it.dataIndex !== 'domain' ), display: protocol === 'TERMINATED_HTTPS' && ssl_parsing_method === 'two-way', @@ -183,7 +181,7 @@ export class Create extends ModalAction { name: 'name', }, ], - columns: certificateColumns, + columns: getCertificateColumns(this), display: protocol === 'TERMINATED_HTTPS' && sni_enabled, }, { diff --git a/src/pages/network/containers/LoadBalancers/StepCreateComponents/ListenerStep/index.jsx b/src/pages/network/containers/LoadBalancers/StepCreateComponents/ListenerStep/index.jsx index e8c47a68..39cc14a0 100644 --- a/src/pages/network/containers/LoadBalancers/StepCreateComponents/ListenerStep/index.jsx +++ b/src/pages/network/containers/LoadBalancers/StepCreateComponents/ListenerStep/index.jsx @@ -15,7 +15,7 @@ import { inject, observer } from 'mobx-react'; import Base from 'components/Form'; import { - certificateColumns, + getCertificateColumns, listenerProtocols, sslParseMethod, } from 'resources/octavia/lb'; @@ -59,9 +59,7 @@ export class ListenerStep extends Base { } get SNISecrets() { - return (this.containersStore.list.data || []).filter( - (it) => !!it.algorithm - ); + return (this.containersStore.list.data || []).filter((it) => !!it.domain); } get defaultValue() { @@ -135,7 +133,7 @@ export class ListenerStep extends Base { name: 'name', }, ], - columns: certificateColumns, + columns: getCertificateColumns(this), display: listener_protocol === 'TERMINATED_HTTPS', }, { @@ -152,8 +150,8 @@ export class ListenerStep extends Base { name: 'name', }, ], - columns: certificateColumns.filter( - (it) => it.dataIndex !== 'algorithm' + columns: getCertificateColumns(this).filter( + (it) => it.dataIndex !== 'domain' ), display: listener_protocol === 'TERMINATED_HTTPS' && @@ -179,7 +177,7 @@ export class ListenerStep extends Base { name: 'name', }, ], - columns: certificateColumns, + columns: getCertificateColumns(this), display: listener_protocol === 'TERMINATED_HTTPS' && listener_sni_enabled, }, diff --git a/src/resources/octavia/lb.js b/src/resources/octavia/lb.js index 0448cb04..875972cf 100644 --- a/src/resources/octavia/lb.js +++ b/src/resources/octavia/lb.js @@ -26,7 +26,7 @@ export const certificateStatus = { ERROR: t('Error'), }; -export const certificateColumns = [ +export const getCertificateColumns = (self) => [ { title: t('Name'), dataIndex: 'name', @@ -43,9 +43,26 @@ export const certificateColumns = [ }, { title: t('Domain Name'), - dataIndex: 'algorithm', + dataIndex: 'domain', render: (value) => value || '-', }, + { + title: t('Listener'), + dataIndex: 'listener', + render: (value) => { + return value + ? self.getLinkRender( + 'lbListenerDetail', + value.name, + { + loadBalancerId: value.lb, + id: value.id, + }, + null + ) + : '-'; + }, + }, { title: t('Status'), dataIndex: 'status', diff --git a/src/stores/barbican/containers.js b/src/stores/barbican/containers.js index 4e791f8b..23cc848d 100644 --- a/src/stores/barbican/containers.js +++ b/src/stores/barbican/containers.js @@ -16,6 +16,7 @@ import Base from 'stores/base'; import client from 'client'; import { action } from 'mobx'; import { SecretsStore } from './secrets'; +import globalListenerStore from '../octavia/listener'; export class ContainersStore extends Base { get client() { @@ -42,11 +43,14 @@ export class ContainersStore extends Base { get mapper() { return (data) => { - const { container_ref } = data; + const { container_ref, algorithm } = data; const [, uuid] = container_ref.split('/containers/'); + const { domain, expiration } = algorithm ? JSON.parse(algorithm) : {}; return { ...data, id: uuid, + domain, + expiration, }; }; } @@ -67,9 +71,32 @@ export class ContainersStore extends Base { return data; } + updateItem(item, listeners) { + const { container_ref } = item; + const enabledLs = listeners.find((ls) => { + const refs = [ + ls.default_tls_container_ref, + ls.client_ca_tls_container_ref, + ...ls.sni_container_refs, + ]; + return refs.includes(container_ref); + }); + if (enabledLs) { + item.listener = { + id: enabledLs.id, + name: enabledLs.name, + lb: enabledLs.lbIds[0], + }; + } + return item; + } + async listDidFetch(items) { if (items.length === 0) return items; - const secrets = await this.secretStore.fetchList({ mode: 'SERVER' }); + const [secrets, listeners] = await Promise.all([ + this.secretStore.fetchList({ mode: 'SERVER' }), + globalListenerStore.fetchList(), + ]); const newItems = items.map((it) => { const { secret_refs = [] } = it; if (secret_refs.length === 0) { @@ -85,12 +112,13 @@ export class ContainersStore extends Base { Object.assign(it, { algorithm: theSecret.algorithm, mode: theSecret.mode, - expiration: theSecret.expiration, }); } else { it.hidden = true; } }); + // Determine if the certificate is used in the listener + this.updateItem(it, listeners); } return { ...it, @@ -101,7 +129,10 @@ export class ContainersStore extends Base { async detailDidFetch(item) { const { secret_refs = [] } = item; - const secrets = await this.secretStore.fetchList({ mode: 'SERVER' }); + const [secrets, listeners] = await Promise.all([ + this.secretStore.fetchList({ mode: 'SERVER' }), + globalListenerStore.fetchList(), + ]); const secretIds = []; // Filter available secrets secret_refs.forEach(async (secret) => { @@ -114,10 +145,11 @@ export class ContainersStore extends Base { Object.assign(item, { algorithm: theSecret.algorithm, mode: theSecret.mode, - expiration: theSecret.expiration, }); } }); + // Determine if the certificate is used in the listener + this.updateItem(item, listeners); // Fetch secrets payload const payloads = await Promise.all( secretIds.map((id) => @@ -138,8 +170,10 @@ export class ContainersStore extends Base { mode: values.mode, payload_content_type: 'text/plain', secret_type: 'certificate', - expiration: values.expiration, - algorithm: values.domain, + algorithm: JSON.stringify({ + domain: values.domain, + expiration: values.expiration, + }), }; const contentData = { ...commonData, diff --git a/src/stores/barbican/secrets.js b/src/stores/barbican/secrets.js index 5da24515..a5ebaddf 100644 --- a/src/stores/barbican/secrets.js +++ b/src/stores/barbican/secrets.js @@ -15,6 +15,7 @@ import Base from 'stores/base'; import client from 'client'; import { action } from 'mobx'; +import globalListenerStore from '../octavia/listener'; export class SecretsStore extends Base { get client() { @@ -42,11 +43,14 @@ export class SecretsStore extends Base { get mapper() { return (data) => { - const { secret_ref } = data; + const { secret_ref, algorithm } = data; const [, uuid] = secret_ref.split('/secrets/'); + const { domain, expiration } = algorithm ? JSON.parse(algorithm) : {}; return { ...data, id: uuid, + domain, + expiration, }; }; } @@ -67,25 +71,70 @@ export class SecretsStore extends Base { return data; } + updateItem(item, listeners) { + const { secret_ref } = item; + const enabledLs = listeners.find((ls) => { + const refs = [ + ls.default_tls_container_ref, + ls.client_ca_tls_container_ref, + ...ls.sni_container_refs, + ]; + return refs.includes(secret_ref); + }); + if (enabledLs) { + item.listener = { + id: enabledLs.id, + name: enabledLs.name, + lb: enabledLs.lbIds[0], + }; + } + return item; + } + @action async fetchDetail({ id, silent }) { if (!silent) { this.isLoading = true; } - const [item, payload] = await Promise.all([ + const [item, payload, listeners] = await Promise.all([ this.client.show(id, {}, { headers: { Accept: 'application/json' } }), this.payloadClient.list(id, {}, { headers: { Accept: 'text/plain' } }), + globalListenerStore.fetchList(), ]); item.payload = payload; + // Determine if the certificate is used in the listener + this.updateItem(item, listeners); const detail = this.mapper(item || {}); this.detail = detail; this.isLoading = false; return detail; } + async listDidFetch(items) { + if (items.length === 0) return items; + const listeners = await globalListenerStore.fetchList(); + return items.map((it) => { + // Determine if the certificate is used in the listener + this.updateItem(it, listeners); + return { + ...it, + }; + }); + } + @action async create(data) { - return this.client.create(data); + const { expiration, domain, algorithm, ...rest } = data; + const body = { + ...rest, + algorithm: + algorithm || + JSON.stringify({ + domain, + expiration, + }), + }; + return this.client.create(body); } }