feat: Update certificate

1. Add certificate id in listener
2. SNI certificate change to multiple select
3. TERMINATED_HTTPS change to HTTPS
4. Change validator of certificate content and keypair
5. Hide domain name if in CA certificate

Change-Id: Id613f5c2c7795a477257ec5f71443378ac686007
This commit is contained in:
xusongfu 2022-05-25 17:46:25 +08:00
parent eae1e27256
commit 1d5c1acb1f
11 changed files with 112 additions and 41 deletions

View File

@ -1069,7 +1069,7 @@
"If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "如果操作系统是Linux系统会修改root用户密码如果是Windows系统会修改Administrator用户密码。", "If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "如果操作系统是Linux系统会修改root用户密码如果是Windows系统会修改Administrator用户密码。",
"If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "若有云主机正在使用此 flavor删除会导致云主机的 flavor 数据缺失,确定删除 {name} ", "If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "若有云主机正在使用此 flavor删除会导致云主机的 flavor 数据缺失,确定删除 {name} ",
"If checked, the network will be enable.": "如果选中,那么网络将被启用。", "If checked, the network will be enable.": "如果选中,那么网络将被启用。",
"If it is an SNI type certificate, a domain name needs to be specified": "如果是 SNI 类型证书,需定域名", "If it is an SNI type certificate, a domain name needs to be specified": "如果是 SNI 类型证书,需定域名",
"If no gateway is specified, the first IP address will be defaulted.": "如果不指定网关IP默认是第一个地址。", "If no gateway is specified, the first IP address will be defaulted.": "如果不指定网关IP默认是第一个地址。",
"If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "如果不选择,那么分配给应用凭证的角色将与当前用户的角色相同。", "If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "如果不选择,那么分配给应用凭证的角色将与当前用户的角色相同。",
"If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "如果计算节点上的nova-compute被禁用将禁止其作为目标节点。", "If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "如果计算节点上的nova-compute被禁用将禁止其作为目标节点。",

View File

@ -20,7 +20,6 @@ import globalContainersStore, {
import { checkPolicyRule } from 'resources/skyline/policy'; import { checkPolicyRule } from 'resources/skyline/policy';
import globalSecretsStore, { SecretsStore } from 'stores/barbican/secrets'; import globalSecretsStore, { SecretsStore } from 'stores/barbican/secrets';
import { certificateMode, certificateStatus } from 'resources/octavia/lb'; import { certificateMode, certificateStatus } from 'resources/octavia/lb';
import { getOptions } from 'utils/index';
import { parse } from 'qs'; import { parse } from 'qs';
import actionConfigs from './actions'; import actionConfigs from './actions';
@ -90,16 +89,20 @@ export class Certificate extends Base {
title: t('Certificate Type'), title: t('Certificate Type'),
dataIndex: 'mode', dataIndex: 'mode',
render: (value) => certificateMode[value] || value, render: (value) => certificateMode[value] || value,
isHideable: true,
}, },
{ {
title: t('Expires At'), title: t('Expires At'),
dataIndex: 'expiration', dataIndex: 'expiration',
valueRender: 'toLocalTime', valueRender: 'toLocalTime',
isHideable: true,
}, },
{ {
title: t('Domain Name'), title: t('Domain Name'),
dataIndex: 'algorithm', dataIndex: 'algorithm',
render: (value) => value || '-', render: (value) => value || '-',
hidden: this.currentMode === 'CA',
isHideable: true,
}, },
{ {
title: t('Status'), title: t('Status'),
@ -110,6 +113,7 @@ export class Certificate extends Base {
title: t('Created At'), title: t('Created At'),
dataIndex: 'created', dataIndex: 'created',
valueRender: 'toLocalTime', valueRender: 'toLocalTime',
isHideable: true,
}, },
]; ];
return columns; return columns;
@ -121,11 +125,6 @@ export class Certificate extends Base {
label: t('Name'), label: t('Name'),
name: 'name', name: 'name',
}, },
{
label: t('Certificate Type'),
name: 'mode',
options: getOptions(certificateMode),
},
]; ];
return ret; return ret;
} }

View File

@ -50,7 +50,7 @@ export class Detail extends Base {
get tabs() { get tabs() {
return [ return [
{ {
title: t('Detail Info'), title: t('BaseDetail'),
key: 'detail_info', key: 'detail_info',
component: BaseDetail, component: BaseDetail,
}, },

View File

@ -50,7 +50,7 @@ export class Detail extends Base {
get tabs() { get tabs() {
return [ return [
{ {
title: t('Detail Info'), title: t('BaseDetail'),
key: 'detail_info', key: 'detail_info',
component: BaseDetail, component: BaseDetail,
}, },

View File

@ -73,18 +73,11 @@ export class CreateAction extends ModalAction {
validateCertificateContent = (rule, value) => { validateCertificateContent = (rule, value) => {
if (!value) return Promise.reject(); if (!value) return Promise.reject();
const keys = value.split(/\n/g); const keys = value.split(/\n/g);
const start = keys.shift(); const start = keys[0];
const end = keys.pop(); const end = keys[keys.length - 1] || keys[keys.length - 2]; // Compatible with last blank line
const middleCorrect = keys.every((it, index) => {
if (index === keys.length - 1) {
return it.length <= 64;
}
return it.length === 64;
});
if ( if (
start === '-----BEGIN CERTIFICATE-----' && start === '-----BEGIN CERTIFICATE-----' &&
end === '-----END CERTIFICATE-----' && end === '-----END CERTIFICATE-----'
middleCorrect
) { ) {
return Promise.resolve(); return Promise.resolve();
} }
@ -98,18 +91,11 @@ export class CreateAction extends ModalAction {
validateCertificateKeyPair = (rule, value) => { validateCertificateKeyPair = (rule, value) => {
if (!value) return Promise.reject(); if (!value) return Promise.reject();
const keys = value.split(/\n/g); const keys = value.split(/\n/g);
const start = keys.shift(); const start = keys[0];
const end = keys.pop(); const end = keys[keys.length - 1] || keys[keys.length - 2];
const middleCorrect = keys.every((it, index) => {
if (index === keys.length - 1) {
return it.length <= 64;
}
return it.length === 64;
});
if ( if (
start === '-----BEGIN RSA PRIVATE KEY-----' && start === '-----BEGIN RSA PRIVATE KEY-----' &&
end === '-----END RSA PRIVATE KEY-----' && end === '-----END RSA PRIVATE KEY-----'
middleCorrect
) { ) {
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -150,7 +150,9 @@ export class Create extends ModalAction {
name: 'name', name: 'name',
}, },
], ],
columns: certificateColumns, columns: certificateColumns.filter(
(it) => it.dataIndex !== 'algorithm'
),
display: display:
protocol === 'TERMINATED_HTTPS' && ssl_parsing_method === 'two-way', protocol === 'TERMINATED_HTTPS' && ssl_parsing_method === 'two-way',
}, },
@ -167,7 +169,7 @@ export class Create extends ModalAction {
required: true, required: true,
data: this.SNICertificate, data: this.SNICertificate,
isLoading: this.containersStore.list.isLoading, isLoading: this.containersStore.list.isLoading,
isMulti: false, isMulti: true,
filterParams: [ filterParams: [
{ {
label: t('Name'), label: t('Name'),
@ -217,9 +219,9 @@ export class Create extends ModalAction {
data.client_authentication = 'MANDATORY'; data.client_authentication = 'MANDATORY';
} }
if (sni_container_refs) { if (sni_container_refs) {
data.sni_container_refs = [ data.sni_container_refs = sni_container_refs.selectedRows.map(
sni_container_refs.selectedRows[0].container_ref, (it) => it.container_ref
]; );
} }
return this.store.create(data); return this.store.create(data);
}; };

View File

@ -46,6 +46,10 @@ export class BaseDetail extends Base {
return [this.PoolInfo, this.healthMonitor]; return [this.PoolInfo, this.healthMonitor];
} }
get rightCards() {
return [this.certificateInfo];
}
get PoolInfo() { get PoolInfo() {
const { default_pool = {} } = this.detailData || {}; const { default_pool = {} } = this.detailData || {};
const { name, protocol, lb_algorithm, description } = default_pool; const { name, protocol, lb_algorithm, description } = default_pool;
@ -106,6 +110,67 @@ export class BaseDetail extends Base {
options, options,
}; };
} }
get certificateInfo() {
const options = [
{
label: t('Server Certificate'),
dataIndex: 'serverCertificateId',
render: (value) => {
return value
? this.getLinkRender(
'certificateContainerDetail',
value,
{
id: value,
},
null
)
: '-';
},
},
{
label: t('CA Certificate'),
dataIndex: 'caCertificateId',
render: (value) => {
return value
? this.getLinkRender(
'certificateSecretDetail',
value,
{
id: value,
},
null
)
: '-';
},
},
{
label: t('SNI Certificate'),
dataIndex: 'sniCertificateId',
render: (value) => {
return value.length
? value.map(
(it, index) =>
this.getLinkRender(
'certificateContainerDetail',
`${it}${index === value.length - 1 ? '' : ' , '}`,
{
id: it,
}
),
null
)
: '-';
},
},
];
return {
title: t('certificate'),
options,
labelCol: 4,
};
}
} }
export default inject('rootStore')(observer(BaseDetail)); export default inject('rootStore')(observer(BaseDetail));

View File

@ -120,9 +120,10 @@ export class StepCreate extends StepAction {
listenerData.client_authentication = 'MANDATORY'; listenerData.client_authentication = 'MANDATORY';
} }
if (listener_sni_enabled && listener_sni_container_refs) { if (listener_sni_enabled && listener_sni_container_refs) {
listenerData.sni_container_refs = [ listenerData.sni_container_refs =
listener_sni_container_refs.selectedRows[0].container_ref, listener_sni_container_refs.selectedRows.map(
]; (it) => it.container_ref
);
} }
} }

View File

@ -152,7 +152,9 @@ export class ListenerStep extends Base {
name: 'name', name: 'name',
}, },
], ],
columns: certificateColumns, columns: certificateColumns.filter(
(it) => it.dataIndex !== 'algorithm'
),
display: display:
listener_protocol === 'TERMINATED_HTTPS' && listener_protocol === 'TERMINATED_HTTPS' &&
listener_ssl_parsing_method === 'two-way', listener_ssl_parsing_method === 'two-way',
@ -170,7 +172,7 @@ export class ListenerStep extends Base {
required: true, required: true,
data: this.SNISecrets, data: this.SNISecrets,
isLoading: false, isLoading: false,
isMulti: false, isMulti: true,
filterParams: [ filterParams: [
{ {
label: t('Name'), label: t('Name'),

View File

@ -79,7 +79,7 @@ export const listenerProtocols = [
value: 'TCP', value: 'TCP',
}, },
{ {
label: 'TERMINATED_HTTPS', label: 'HTTPS',
value: 'TERMINATED_HTTPS', value: 'TERMINATED_HTTPS',
}, },
{ {

View File

@ -47,7 +47,23 @@ export class ListenerStore extends Base {
// } // }
async detailDidFetch(item) { async detailDidFetch(item) {
const { default_pool_id } = item; const {
default_pool_id,
default_tls_container_ref = '',
client_ca_tls_container_ref = '',
sni_container_refs = [],
} = item;
const [, serverId] = default_tls_container_ref.split('/containers/');
const [, caId] = client_ca_tls_container_ref.split('/secrets/');
const sniId = sni_container_refs.map((it) => {
const [, ssid] = it.split('/containers/');
return ssid;
});
Object.assign(item, {
serverCertificateId: serverId,
caCertificateId: caId,
sniCertificateId: sniId,
});
if (default_pool_id) { if (default_pool_id) {
// pool attach listener or loadbalancer // pool attach listener or loadbalancer
try { try {