
This change adds barbican panel for secrets and list similar to other panels and subpanel. Has Creation , Detail Secret Page included Change-Id: I33a7c4a4845529c5a6d44060728956701f50dccf Signed-off-by: Reet Srivastava <reet.srivastava@rackspace.com>
463 lines
11 KiB
JavaScript
463 lines
11 KiB
JavaScript
// Copyright 2025 99cloud
|
|
//
|
|
// 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 globalSecretsStore from 'stores/barbican/secrets';
|
|
import globalOrdersStore from 'stores/barbican/orders';
|
|
import moment from 'moment';
|
|
|
|
// Constants for secret configuration
|
|
const SECRET_CONFIG = {
|
|
KEY: {
|
|
BIT_LENGTH: 256,
|
|
ALGORITHM: 'aes',
|
|
},
|
|
ASYMMETRIC: {
|
|
BIT_LENGTH: 2048,
|
|
ALGORITHM: 'rsa',
|
|
},
|
|
};
|
|
|
|
export class CreateSecret extends ModalAction {
|
|
static id = 'create-secret';
|
|
|
|
static title = t('Create Secret');
|
|
|
|
static policy = ['secrets:post', 'orders:post'];
|
|
|
|
static allowed = () => Promise.resolve(true);
|
|
|
|
static get modalSize() {
|
|
return 'large';
|
|
}
|
|
|
|
static CONTENT_TYPE_OPTIONS = [
|
|
{ label: 'text/plain', value: 'text/plain' },
|
|
{ label: 'application/octet-stream', value: 'application/octet-stream' },
|
|
{ label: 'application/x-pkcs12', value: 'application/x-pkcs12' },
|
|
{ label: 'application/x-pem-file', value: 'application/x-pem-file' },
|
|
];
|
|
|
|
static ALGORITHM_OPTIONS = [
|
|
{ label: 'AES', value: 'aes' },
|
|
{ label: 'DES', value: 'des' },
|
|
{ label: '3DES', value: '3des' },
|
|
{ label: 'RSA', value: 'rsa' },
|
|
{ label: 'DSA', value: 'dsa' },
|
|
{ label: 'EC', value: 'ec' },
|
|
];
|
|
|
|
static ASYMMETRIC_ALGORITHM_OPTIONS = [
|
|
{ label: 'RSA', value: 'rsa' },
|
|
{ label: 'DSA', value: 'dsa' },
|
|
];
|
|
|
|
static BIT_LENGTH_OPTIONS = [
|
|
{ label: '128', value: 128 },
|
|
{ label: '192', value: 192 },
|
|
{ label: '256', value: 256 },
|
|
{ label: '1024', value: 1024 },
|
|
{ label: '2048', value: 2048 },
|
|
{ label: '4096', value: 4096 },
|
|
];
|
|
|
|
static MODE_OPTIONS = [
|
|
{ label: 'CBC', value: 'cbc' },
|
|
{ label: 'CTR', value: 'ctr' },
|
|
];
|
|
|
|
init() {
|
|
this.secretsStore = globalSecretsStore;
|
|
this.ordersStore = globalOrdersStore;
|
|
}
|
|
|
|
get name() {
|
|
return t('Create Secret');
|
|
}
|
|
|
|
get nameForStateUpdate() {
|
|
return ['creationType', 'secret_type', 'request_type', 'algorithm'];
|
|
}
|
|
|
|
onSecretTypeChange = (value) => {
|
|
const { creationType } = this.state;
|
|
|
|
if (!this.formRef.current) {
|
|
return;
|
|
}
|
|
|
|
if (creationType === 'direct') {
|
|
this.formRef.current.setFieldsValue({
|
|
secret_type: value,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const config =
|
|
value === 'key'
|
|
? SECRET_CONFIG.KEY
|
|
: value === 'asymmetric'
|
|
? SECRET_CONFIG.ASYMMETRIC
|
|
: { BIT_LENGTH: undefined, ALGORITHM: undefined };
|
|
|
|
this.formRef.current.setFieldsValue({
|
|
request_type: value,
|
|
bit_length: config.BIT_LENGTH,
|
|
algorithm: config.ALGORITHM,
|
|
});
|
|
};
|
|
|
|
get defaultValue() {
|
|
return {
|
|
creationType: 'direct',
|
|
name: '',
|
|
payload: '',
|
|
payload_content_type: 'text/plain',
|
|
payload_content_encoding: '',
|
|
algorithm: 'aes',
|
|
bit_length: 256,
|
|
mode: 'cbc',
|
|
secret_type: 'opaque',
|
|
request_type: 'key',
|
|
expiration: '',
|
|
};
|
|
}
|
|
|
|
get creationTypeOptions() {
|
|
return [
|
|
{
|
|
label: t('Create Secret Directly'),
|
|
value: 'direct',
|
|
tip: t('Create a secret with your own payload content'),
|
|
},
|
|
{
|
|
label: t('Create Secret via Order'),
|
|
value: 'order',
|
|
tip: t('Create a secret through order (for certificates, keys, etc.)'),
|
|
},
|
|
];
|
|
}
|
|
|
|
get requestTypeOptions() {
|
|
return [
|
|
{
|
|
label: t('Key'),
|
|
value: 'key',
|
|
tip: t('Symmetric keys, private keys, and passphrases'),
|
|
},
|
|
{
|
|
label: t('Asymmetric Key'),
|
|
value: 'asymmetric',
|
|
tip: t('Asymmetric key pairs (public/private)'),
|
|
},
|
|
];
|
|
}
|
|
|
|
get secretTypeOptionsForDirect() {
|
|
return [
|
|
{
|
|
label: t('Opaque'),
|
|
value: 'opaque',
|
|
tip: t('Default secret type for arbitrary data'),
|
|
},
|
|
{
|
|
label: t('Symmetric'),
|
|
value: 'symmetric',
|
|
tip: t('Symmetric keys'),
|
|
},
|
|
{
|
|
label: t('Public'),
|
|
value: 'public',
|
|
tip: t('Public keys'),
|
|
},
|
|
{
|
|
label: t('Private'),
|
|
value: 'private',
|
|
tip: t('Private keys'),
|
|
},
|
|
{
|
|
label: t('Certificate'),
|
|
value: 'certificate',
|
|
tip: t('Certificates'),
|
|
},
|
|
{
|
|
label: t('Passphrase'),
|
|
value: 'passphrase',
|
|
tip: t('Passphrases'),
|
|
},
|
|
];
|
|
}
|
|
|
|
get algorithmOptions() {
|
|
const { secret_type, request_type } = this.state;
|
|
const currentType = secret_type || request_type;
|
|
|
|
if (currentType === 'asymmetric') {
|
|
return CreateSecret.ASYMMETRIC_ALGORITHM_OPTIONS;
|
|
}
|
|
|
|
return CreateSecret.ALGORITHM_OPTIONS;
|
|
}
|
|
|
|
get bitLengthOptions() {
|
|
return CreateSecret.BIT_LENGTH_OPTIONS;
|
|
}
|
|
|
|
get modeOptions() {
|
|
return CreateSecret.MODE_OPTIONS;
|
|
}
|
|
|
|
get formItems() {
|
|
const { creationType } = this.state;
|
|
const isDirect = creationType === 'direct';
|
|
const isOrder = creationType === 'order';
|
|
|
|
const baseItems = this.getBaseFormItems();
|
|
|
|
if (isDirect) {
|
|
return [...baseItems, ...this.getDirectFormItems()];
|
|
}
|
|
|
|
if (isOrder) {
|
|
return [...baseItems, ...this.getOrderFormItems()];
|
|
}
|
|
|
|
return baseItems;
|
|
}
|
|
|
|
getBaseFormItems() {
|
|
const { creationType } = this.state;
|
|
const isDirect = creationType === 'direct';
|
|
const isOrder = creationType === 'order';
|
|
|
|
return [
|
|
{
|
|
name: 'creationType',
|
|
label: t('Creation Method'),
|
|
type: 'radio',
|
|
options: this.creationTypeOptions,
|
|
required: true,
|
|
},
|
|
{
|
|
name: 'name',
|
|
label: t('Secret Name'),
|
|
type: 'input-name',
|
|
required: false,
|
|
withoutChinese: true,
|
|
tip: isOrder
|
|
? t('Optional. If not provided, a name will be auto-generated')
|
|
: t('Required for direct creation'),
|
|
rules: isDirect
|
|
? [
|
|
{
|
|
required: true,
|
|
message: t('Secret name is required for direct creation'),
|
|
},
|
|
]
|
|
: [],
|
|
},
|
|
];
|
|
}
|
|
|
|
getDirectFormItems() {
|
|
return [
|
|
{
|
|
name: 'payload',
|
|
label: t('Secret Payload'),
|
|
type: 'textarea',
|
|
required: true,
|
|
rows: 6,
|
|
placeholder: t('Enter the secret content'),
|
|
tip: t('The actual secret data to be stored'),
|
|
},
|
|
{
|
|
name: 'payload_content_type',
|
|
label: t('Payload Content Type'),
|
|
type: 'select',
|
|
options: CreateSecret.CONTENT_TYPE_OPTIONS,
|
|
required: true,
|
|
tip: t('Required when payload is supplied'),
|
|
},
|
|
{
|
|
name: 'payload_content_encoding',
|
|
label: t('Payload Content Encoding'),
|
|
type: 'input',
|
|
required: false,
|
|
tip: t(
|
|
'Required if payload content type is "application/octet-stream"'
|
|
),
|
|
},
|
|
{
|
|
name: 'secret_type',
|
|
label: t('Secret Type'),
|
|
type: 'select',
|
|
options: [
|
|
{
|
|
label: t('Select Secret Type'),
|
|
value: '',
|
|
},
|
|
...this.secretTypeOptionsForDirect,
|
|
],
|
|
required: false,
|
|
tip: t('Optional. Type of secret being stored (default: opaque)'),
|
|
},
|
|
{
|
|
name: 'algorithm',
|
|
label: t('Algorithm'),
|
|
type: 'select',
|
|
options: [
|
|
{
|
|
label: t('Select Algorithm'),
|
|
value: '',
|
|
},
|
|
...this.algorithmOptions,
|
|
],
|
|
required: false,
|
|
tip: t('Optional. Algorithm used by the secret (default: aes)'),
|
|
},
|
|
{
|
|
name: 'bit_length',
|
|
label: t('Bit Length'),
|
|
type: 'select',
|
|
options: [
|
|
{
|
|
label: t('Select Bit Length'),
|
|
value: '',
|
|
},
|
|
...this.bitLengthOptions,
|
|
],
|
|
required: false,
|
|
tip: t('Optional. Bit length of the secret (default: 256)'),
|
|
},
|
|
{
|
|
name: 'mode',
|
|
label: t('Mode'),
|
|
type: 'select',
|
|
options: [
|
|
{
|
|
label: t('Select Mode'),
|
|
value: '',
|
|
},
|
|
...this.modeOptions,
|
|
],
|
|
required: false,
|
|
tip: t(
|
|
'Optional. Algorithm mode, used only for reference (default: cbc)'
|
|
),
|
|
},
|
|
{
|
|
name: 'expiration',
|
|
label: t('Expiration Date'),
|
|
type: 'date-picker',
|
|
showToday: false,
|
|
disabledDate: (current) => current && current <= moment().endOf('d'),
|
|
tip: t('Optional. When the secret should expire (ISO 8601 format)'),
|
|
},
|
|
];
|
|
}
|
|
|
|
getOrderFormItems() {
|
|
return [
|
|
{
|
|
name: 'request_type',
|
|
label: t('Request Type'),
|
|
type: 'select',
|
|
options: this.requestTypeOptions,
|
|
required: true,
|
|
onChange: this.onSecretTypeChange,
|
|
},
|
|
{
|
|
name: 'algorithm',
|
|
label: t('Algorithm'),
|
|
type: 'select',
|
|
options: this.algorithmOptions,
|
|
required: false,
|
|
tip: t(
|
|
'Optional. Algorithm to be used with the requested key (default: aes)'
|
|
),
|
|
},
|
|
{
|
|
name: 'bit_length',
|
|
label: t('Bit Length'),
|
|
type: 'select',
|
|
options: this.bitLengthOptions,
|
|
required: false,
|
|
tip: t(
|
|
'Optional. Bit length of the requested secret key (default: 256)'
|
|
),
|
|
},
|
|
{
|
|
name: 'mode',
|
|
label: t('Mode'),
|
|
type: 'select',
|
|
options: this.modeOptions,
|
|
required: false,
|
|
tip: t(
|
|
'Optional. Algorithm mode to be used with the requested key (default: cbc)'
|
|
),
|
|
},
|
|
{
|
|
name: 'payload_content_type',
|
|
label: t('Payload Content Type'),
|
|
type: 'select',
|
|
options: [
|
|
{
|
|
label: 'application/octet-stream',
|
|
value: 'application/octet-stream',
|
|
},
|
|
{ label: 'text/plain', value: 'text/plain' },
|
|
{ label: 'application/x-pkcs12', value: 'application/x-pkcs12' },
|
|
{
|
|
label: 'application/x-pem-file',
|
|
value: 'application/x-pem-file',
|
|
},
|
|
],
|
|
required: false,
|
|
tip: t(
|
|
'Optional. Type/format of the secret to be generated (default: application/octet-stream)'
|
|
),
|
|
},
|
|
{
|
|
name: 'expiration',
|
|
label: t('Expiration Date'),
|
|
type: 'date-picker',
|
|
showToday: false,
|
|
disabledDate: (current) => current && current <= moment().endOf('d'),
|
|
tip: t('Optional. Expiration time for the secret in ISO 8601 format'),
|
|
},
|
|
];
|
|
}
|
|
|
|
onClickCancel = () => {
|
|
this.onCancel();
|
|
};
|
|
|
|
onSubmit = (values) => {
|
|
const { creationType, ...rest } = values;
|
|
|
|
if (creationType === 'direct') {
|
|
return this.secretsStore.create(rest);
|
|
}
|
|
|
|
if (creationType === 'order') {
|
|
return this.ordersStore.create(rest);
|
|
}
|
|
|
|
return Promise.reject(new Error('Invalid creation type'));
|
|
};
|
|
}
|
|
|
|
export default inject('rootStore')(observer(CreateSecret));
|