feat: Add application credential
add application credential Change-Id: Id07525e3bc8709aa4226dabd8084d9a09821c397
This commit is contained in:
parent
360f387aa8
commit
a6f387d67e
@ -105,6 +105,11 @@ class KeystoneClient extends Base {
|
||||
{
|
||||
key: 'groups',
|
||||
},
|
||||
{
|
||||
name: 'applicationCredentials',
|
||||
key: 'application_credentials',
|
||||
responseKey: 'application_credential',
|
||||
},
|
||||
],
|
||||
extendOperations: [
|
||||
{
|
||||
|
@ -12,19 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
import React from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { Typography } from 'antd';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import { allCanReadPolicy } from 'resources/policy';
|
||||
import globalAuthCatalogStore from 'stores/keystone/catalog';
|
||||
import { getOpenRc } from 'resources/openstack-rc';
|
||||
import { getCredentialOpenRc, getPwdOpenRc } from 'resources/openstack-rc';
|
||||
import FileSaver from 'file-saver';
|
||||
import styles from './index.less';
|
||||
// import { DownSquareOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
@inject('rootStore')
|
||||
@observer
|
||||
@ -37,67 +30,20 @@ export default class OpenRc extends ModalAction {
|
||||
|
||||
static title = t('Get OpenRC file');
|
||||
|
||||
componentDidMount() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
async getData() {
|
||||
await this.store.fetchList({});
|
||||
this.exportRcFile();
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Get OpenRC file');
|
||||
}
|
||||
|
||||
getModalSize() {
|
||||
return 'large';
|
||||
}
|
||||
|
||||
get token() {
|
||||
const key = 'keystone_token';
|
||||
const item = localStorage.getItem(key);
|
||||
try {
|
||||
return JSON.parse(item) || {};
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
get showNotice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
get tokenValue() {
|
||||
return this.token.value || '';
|
||||
}
|
||||
|
||||
get tokenExpiry() {
|
||||
const { expires } = this.token;
|
||||
return expires || 0;
|
||||
}
|
||||
|
||||
get user() {
|
||||
const { user } = this.props.rootStore;
|
||||
return user;
|
||||
}
|
||||
|
||||
get project() {
|
||||
const {
|
||||
project: {
|
||||
id: projectId = '',
|
||||
name: projectName = '',
|
||||
domain: { name: projectDomainName } = {},
|
||||
} = {},
|
||||
} = this.user || {};
|
||||
return {
|
||||
projectId,
|
||||
projectName,
|
||||
projectDomainName,
|
||||
};
|
||||
}
|
||||
|
||||
get openRc() {
|
||||
getOpenRC(type) {
|
||||
const {
|
||||
project: {
|
||||
id: projectId = '',
|
||||
@ -114,7 +60,9 @@ export default class OpenRc extends ModalAction {
|
||||
const authUrl = endpoints.filter(
|
||||
(endpoint) => endpoint.interface === 'public'
|
||||
)[0].url;
|
||||
const openstackRc = getOpenRc({
|
||||
let openstackRc = '';
|
||||
if (type === 'password') {
|
||||
openstackRc = getPwdOpenRc({
|
||||
authUrl,
|
||||
projectId,
|
||||
projectName,
|
||||
@ -123,11 +71,18 @@ export default class OpenRc extends ModalAction {
|
||||
userName,
|
||||
region,
|
||||
});
|
||||
} else {
|
||||
openstackRc = getCredentialOpenRc({
|
||||
authUrl,
|
||||
region,
|
||||
});
|
||||
}
|
||||
|
||||
return openstackRc;
|
||||
}
|
||||
|
||||
exportRcFile = () => {
|
||||
const blob = new Blob([this.openRc], {
|
||||
exportRcFile = (data) => {
|
||||
const blob = new Blob([data], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
});
|
||||
FileSaver.saveAs(blob, 'openrc.sh');
|
||||
@ -135,7 +90,7 @@ export default class OpenRc extends ModalAction {
|
||||
|
||||
get defaultValue() {
|
||||
const value = {
|
||||
token: this.tokenValue,
|
||||
type: 'password',
|
||||
};
|
||||
return value;
|
||||
}
|
||||
@ -144,34 +99,30 @@ export default class OpenRc extends ModalAction {
|
||||
|
||||
static allowed = () => Promise.resolve(true);
|
||||
|
||||
get labelCol() {
|
||||
return {
|
||||
xs: { span: 0 },
|
||||
sm: { span: 0 },
|
||||
};
|
||||
}
|
||||
|
||||
get wrapperCol() {
|
||||
return {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 24 },
|
||||
};
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'token',
|
||||
label: '',
|
||||
type: 'label',
|
||||
component: (
|
||||
<Paragraph copyable={{ text: this.openRc }} className={styles.token}>
|
||||
<pre>{this.openRc}</pre>
|
||||
</Paragraph>
|
||||
),
|
||||
name: 'type',
|
||||
label: t('Type'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: t('Password Type'),
|
||||
value: 'password',
|
||||
},
|
||||
{
|
||||
label: t('Credential Type'),
|
||||
value: 'credential',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
onSubmit = () => Promise.resolve();
|
||||
onSubmit = (values) => {
|
||||
const { type } = values;
|
||||
return this.store.fetchList().then(() => {
|
||||
return this.exportRcFile(this.getOpenRC(type));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import { getNoValue } from 'utils/index';
|
||||
import { columnRender } from 'utils/render';
|
||||
import { getLocalStorageItem, setLocalStorageItem } from 'utils/local-storage';
|
||||
import { inject } from 'mobx-react';
|
||||
import globalRootStore from 'stores/root';
|
||||
import CustomColumns from './CustomColumns';
|
||||
import ItemActionButtons from './ItemActionButtons';
|
||||
import PrimaryActionButtons from './PrimaryActionButtons';
|
||||
@ -331,7 +332,11 @@ export default class BaseTable extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{globalRootStore.hasAdminRole ? (
|
||||
<Link to={url}>{projectId}</Link>
|
||||
) : (
|
||||
projectId
|
||||
)}
|
||||
</div>
|
||||
<div>{value || '-'}</div>
|
||||
</>
|
||||
@ -360,7 +365,14 @@ export default class BaseTable extends React.Component {
|
||||
if (render) {
|
||||
return render;
|
||||
}
|
||||
const { linkPrefix, dataIndex, idKey, linkPrefixFunc, linkFunc } = column;
|
||||
const {
|
||||
linkPrefix,
|
||||
dataIndex,
|
||||
idKey,
|
||||
linkPrefixFunc,
|
||||
linkFunc,
|
||||
hasNoDetail = false,
|
||||
} = column;
|
||||
const { rowKey } = this.props;
|
||||
return (value, record) => {
|
||||
const idValue = get(record, idKey || rowKey);
|
||||
@ -374,7 +386,15 @@ export default class BaseTable extends React.Component {
|
||||
url = this.getLinkUrl(linkValue, idValue);
|
||||
}
|
||||
const nameValue = value || get(record, dataIndex) || '-';
|
||||
if (!url) {
|
||||
if (hasNoDetail) {
|
||||
return (
|
||||
<div>
|
||||
<div>{idValue}</div>
|
||||
<div>{nameValue}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!url && !hasNoDetail) {
|
||||
return nameValue;
|
||||
}
|
||||
return (
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { HomeOutlined, UserOutlined } from '@ant-design/icons';
|
||||
|
||||
const renderMenu = (t) => {
|
||||
if (!t) {
|
||||
@ -29,6 +29,15 @@ const renderMenu = (t) => {
|
||||
hasBreadcrumb: false,
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
path: '/user/application-credentials',
|
||||
name: t('Application Credentials'),
|
||||
key: '/user/application-credentials',
|
||||
level: 0,
|
||||
icon: <UserOutlined />,
|
||||
children: [],
|
||||
hasChildren: false,
|
||||
},
|
||||
];
|
||||
return menu;
|
||||
};
|
||||
|
@ -94,6 +94,7 @@
|
||||
"Anti-affinity (not mandatory):": "Anti-affinity (not mandatory):",
|
||||
"Any": "Any",
|
||||
"Any(Random)": "Any(Random)",
|
||||
"Application Credentials": "Application Credentials",
|
||||
"Application Template": "Application Template",
|
||||
"Apply for extended quota": "Apply for extended quota",
|
||||
"Applying": "Applying",
|
||||
@ -299,6 +300,7 @@
|
||||
"Create": "Create",
|
||||
"Create ": "Create ",
|
||||
"Create Allowed Address Pair": "Create Allowed Address Pair",
|
||||
"Create Application Credentials": "Create Application Credentials",
|
||||
"Create Backup": "Create Backup",
|
||||
"Create Bandwidth Limit Rule": "Create Bandwidth Limit Rule",
|
||||
"Create Bare Metal Node": "Create Bare Metal Node",
|
||||
@ -367,6 +369,7 @@
|
||||
"Created Time": "Created Time",
|
||||
"Creating": "Creating",
|
||||
"Creation Timeout (Minutes)": "Creation Timeout (Minutes)",
|
||||
"Credential Type": "Credential Type",
|
||||
"Current Availability Zones": "Current Availability Zones",
|
||||
"Current Capacity (GB)": "Current Capacity (GB)",
|
||||
"Current Compute Host": "Current Compute Host",
|
||||
@ -416,6 +419,7 @@
|
||||
"Delay Interval(s)": "Delay Interval(s)",
|
||||
"Delete": "Delete",
|
||||
"Delete Allowed Address Pair": "Delete Allowed Address Pair",
|
||||
"Delete Application Credential": "Delete Application Credential",
|
||||
"Delete Backup": "Delete Backup",
|
||||
"Delete Bandwidth Egress Rules": "Delete Bandwidth Egress Rules",
|
||||
"Delete Bandwidth Ingress Rules": "Delete Bandwidth Ingress Rules",
|
||||
@ -612,6 +616,7 @@
|
||||
"Execution Result": "Execution Result",
|
||||
"Expand Advanced Options": "Expand Advanced Options",
|
||||
"Expired Time": "Expired Time",
|
||||
"Expires At": "Expires At",
|
||||
"Extend Root Volume": "Extend Root Volume",
|
||||
"Extend Volume": "Extend Volume",
|
||||
"Extend root volume": "Extend root volume",
|
||||
@ -1109,6 +1114,7 @@
|
||||
"Parameter": "Parameter",
|
||||
"Params Setting": "Params Setting",
|
||||
"Password": "Password",
|
||||
"Password Type": "Password Type",
|
||||
"Password changed successfully, please log in again.": "Password changed successfully, please log in again.",
|
||||
"Password must be the same with confirm password.": "Password must be the same with confirm password.",
|
||||
"Pause": "Pause",
|
||||
@ -1140,7 +1146,6 @@
|
||||
"Placement service:": "Placement service:",
|
||||
"Platform Info": "Platform Info",
|
||||
"Please confirm your password!": "Please confirm your password!",
|
||||
"Please do not request repeat quickly": "Please do not request repeat quickly",
|
||||
"Please enter a valid Email Address!": "Please enter a valid Email Address!",
|
||||
"Please enter a valid Phone Number": "Please enter a valid Phone Number",
|
||||
"Please enter complete key value!": "Please enter complete key value!",
|
||||
@ -1521,15 +1526,13 @@
|
||||
"Switch Info": "Switch Info",
|
||||
"Switch Language": "Switch Language",
|
||||
"Switch Project": "Switch Project",
|
||||
"Syetem Admin": "Syetem Admin",
|
||||
"Syetem Reader": "Syetem Reader",
|
||||
"System": "System",
|
||||
"System Admin": "System Admin",
|
||||
"System Config": "System Config",
|
||||
"System Disk": "System Disk",
|
||||
"System Info": "System Info",
|
||||
"System Reader": "System Reader",
|
||||
"System Scope": "System Scope",
|
||||
"System error": "System error",
|
||||
"System is busy, please try again later": "System is busy, please try again later",
|
||||
"System is error, please try again later.": "System is error, please try again later.",
|
||||
"TCP": "TCP",
|
||||
"Tags": "Tags",
|
||||
@ -1788,6 +1791,7 @@
|
||||
"add router": "add router",
|
||||
"all": "all",
|
||||
"an optional string field to be used to store any vendor-specific information": "an optional string field to be used to store any vendor-specific information",
|
||||
"application credential": "application credential",
|
||||
"associate floating ip": "associate floating ip",
|
||||
"attach interface": "attach interface",
|
||||
"availability zones": "availability zones",
|
||||
@ -1835,6 +1839,7 @@
|
||||
"delete DNAT rule": "delete DNAT rule",
|
||||
"delete SNAT rule": "delete SNAT rule",
|
||||
"delete allowed address pair": "delete allowed address pair",
|
||||
"delete application credential": "delete application credential",
|
||||
"delete backup": "delete backup",
|
||||
"delete bandwidth egress rules": "delete bandwidth egress rules",
|
||||
"delete bandwidth ingress rules": "delete bandwidth ingress rules",
|
||||
|
@ -94,6 +94,7 @@
|
||||
"Anti-affinity (not mandatory):": "反亲和 (非强制):",
|
||||
"Any": "",
|
||||
"Any(Random)": "任意(随机)",
|
||||
"Application Credentials": "应用凭证",
|
||||
"Application Template": "应用模板",
|
||||
"Apply for extended quota": "申请扩展配额",
|
||||
"Applying": "使用中",
|
||||
@ -299,6 +300,7 @@
|
||||
"Create": "创建",
|
||||
"Create ": "创建",
|
||||
"Create Allowed Address Pair": "创建可用地址对",
|
||||
"Create Application Credentials": "创建应用凭证",
|
||||
"Create Backup": "创建备份",
|
||||
"Create Bandwidth Limit Rule": "创建带宽限制规则",
|
||||
"Create Bare Metal Node": "创建裸机节点",
|
||||
@ -367,6 +369,7 @@
|
||||
"Created Time": "创建时间",
|
||||
"Creating": "创建中",
|
||||
"Creation Timeout (Minutes)": "创建超时(分钟)",
|
||||
"Credential Type": "凭证类型",
|
||||
"Current Availability Zones": "当前可用域",
|
||||
"Current Capacity (GB)": "当前容量(GB)",
|
||||
"Current Compute Host": "当前计算节点",
|
||||
@ -416,6 +419,7 @@
|
||||
"Delay Interval(s)": "检查间隔(秒)",
|
||||
"Delete": "删除",
|
||||
"Delete Allowed Address Pair": "删除可用地址对",
|
||||
"Delete Application Credential": "删除应用凭证",
|
||||
"Delete Backup": "删除备份",
|
||||
"Delete Bandwidth Egress Rules": "删除带宽出方向限制",
|
||||
"Delete Bandwidth Ingress Rules": "删除带宽入方向限制",
|
||||
@ -612,6 +616,7 @@
|
||||
"Execution Result": "执行结果",
|
||||
"Expand Advanced Options": "展开高级选项",
|
||||
"Expired Time": "到期时间",
|
||||
"Expires At": "到期时间",
|
||||
"Extend Root Volume": "扩容根硬盘",
|
||||
"Extend Volume": "扩容云硬盘",
|
||||
"Extend root volume": "扩容根硬盘",
|
||||
@ -1108,6 +1113,7 @@
|
||||
"Parameter": "参数",
|
||||
"Params Setting": "参数设置",
|
||||
"Password": "密码",
|
||||
"Password Type": "密码类型",
|
||||
"Password changed successfully, please log in again.": "密码修改成功,请重新登录。",
|
||||
"Password must be the same with confirm password.": "密码和确认密码必须一致。",
|
||||
"Pause": "暂停",
|
||||
@ -1139,7 +1145,6 @@
|
||||
"Placement service:": "放置服务(placement):",
|
||||
"Platform Info": "平台概况",
|
||||
"Please confirm your password!": "请确认您的密码",
|
||||
"Please do not request repeat quickly": "",
|
||||
"Please enter a valid Email Address!": "请输入一个有效的邮箱地址",
|
||||
"Please enter a valid Phone Number": "请输入一个有效的手机号",
|
||||
"Please enter complete key value!": "请输入完整的键值!",
|
||||
@ -1520,15 +1525,13 @@
|
||||
"Switch Info": "交换机信息",
|
||||
"Switch Language": "切换语言",
|
||||
"Switch Project": "切换项目",
|
||||
"Syetem Admin": "系统管理权限",
|
||||
"Syetem Reader": "系统只读权限",
|
||||
"System": "系统",
|
||||
"System Admin": "系统管理权限",
|
||||
"System Config": "系统配置",
|
||||
"System Disk": "系统盘",
|
||||
"System Info": "系统信息",
|
||||
"System Reader": "系统只读权限",
|
||||
"System Scope": "绑定系统范围",
|
||||
"System error": "",
|
||||
"System is busy, please try again later": "",
|
||||
"System is error, please try again later.": "系统出错,请稍后再试。",
|
||||
"TCP": "",
|
||||
"Tags": "标签",
|
||||
@ -1787,6 +1790,7 @@
|
||||
"add router": "添加路由器",
|
||||
"all": "所有",
|
||||
"an optional string field to be used to store any vendor-specific information": "选填选型,用于储存供应商的特定信息",
|
||||
"application credential": "应用凭证",
|
||||
"associate floating ip": "绑定浮动IP",
|
||||
"attach interface": "挂载网卡",
|
||||
"availability zones": "可用域",
|
||||
@ -1834,6 +1838,7 @@
|
||||
"delete DNAT rule": "删除DNAT规则",
|
||||
"delete SNAT rule": "删除SNAT规则",
|
||||
"delete allowed address pair": "删除可用地址对",
|
||||
"delete application credential": "删除应用凭证",
|
||||
"delete backup": "删除备份",
|
||||
"delete bandwidth egress rules": "删除出方向带宽限制规则",
|
||||
"delete bandwidth ingress rules": "删除入方向带宽限制规则",
|
||||
@ -1978,7 +1983,6 @@
|
||||
"subnet": "子网",
|
||||
"subnets": "子网",
|
||||
"suspend instance": "挂起云主机",
|
||||
"the instance only has one virtual adapter": "云主机只有一个网卡",
|
||||
"the policy is in use": "策略正在使用中",
|
||||
"the router has connected subnet": "路由器有连接的子网",
|
||||
"the vpn gateway is in use": "VPN网关正在使用中",
|
||||
|
@ -18,6 +18,7 @@ import { Badge } from 'antd';
|
||||
import { UserStore } from 'stores/keystone/user';
|
||||
import globalDomainStore from 'stores/keystone/domain';
|
||||
import Base from 'containers/TabDetail';
|
||||
import Credentials from 'src/pages/user-center/containers/Credentials';
|
||||
import UserGroup from '../../UserGroup';
|
||||
import Project from '../../Project';
|
||||
import actionConfigs from '../actions';
|
||||
@ -128,6 +129,11 @@ export class UserDetail extends Base {
|
||||
key: 'userGroup',
|
||||
component: UserGroup,
|
||||
},
|
||||
{
|
||||
title: t('Application Credentials'),
|
||||
key: 'applicationCredentials',
|
||||
component: Credentials,
|
||||
},
|
||||
];
|
||||
return tabs;
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
// Copyright 2021 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 globalCredentialStore from 'stores/keystone/credential';
|
||||
import moment from 'moment';
|
||||
import globalRootStore from 'stores/root';
|
||||
import { toJS } from 'mobx';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
@inject('rootStore')
|
||||
@observer
|
||||
export default class Create extends ModalAction {
|
||||
static id = 'create-application_credentials';
|
||||
|
||||
static title = t('Create Application Credentials');
|
||||
|
||||
get name() {
|
||||
return t('Create Application Credentials');
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
if (values.expires_at) {
|
||||
values.expires_at = values.expires_at.clone().endOf('day');
|
||||
}
|
||||
if (values.roles) {
|
||||
values.roles = Object.keys(values.roles)
|
||||
.filter((key) => values.roles[key])
|
||||
.map((i) => ({ id: i }));
|
||||
}
|
||||
return globalCredentialStore.create(values).then((res) => {
|
||||
const { links, roles, system, unrestricted, user_id, name, ...rest } =
|
||||
res.application_credential;
|
||||
const filename = `${name}.json`;
|
||||
const blob = new Blob([JSON.stringify(rest, null, 2)], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
});
|
||||
FileSaver.saveAs(blob, filename);
|
||||
});
|
||||
};
|
||||
|
||||
static allowed() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
static policy = 'identity:create_application_credential';
|
||||
|
||||
get roleOptions() {
|
||||
// const baseRoles = toJS(globalRootStore.baseRoles);
|
||||
const roles = toJS(globalRootStore.roles);
|
||||
|
||||
return roles.map((i) => ({ label: i.name, value: i.id }));
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Name'),
|
||||
type: 'input-name',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'expires_at',
|
||||
label: t('Expires At'),
|
||||
type: 'date-picker',
|
||||
showToday: false,
|
||||
disabledDate: (current) =>
|
||||
current && current < moment().subtract(1, 'days').endOf('d'),
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'roles',
|
||||
label: t('Roles'),
|
||||
type: 'check-group',
|
||||
options: this.roleOptions,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: t('Description'),
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2021 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 { ConfirmAction } from 'containers/Action';
|
||||
import globalCredentialStore from 'stores/keystone/credential';
|
||||
|
||||
export default class DeleteAction extends ConfirmAction {
|
||||
get id() {
|
||||
return 'delete';
|
||||
}
|
||||
|
||||
get title() {
|
||||
return t('Delete Application Credential');
|
||||
}
|
||||
|
||||
get buttonType() {
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
get buttonText() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
get actionName() {
|
||||
return t('delete application credential');
|
||||
}
|
||||
|
||||
policy = 'identity:delete_application_credential';
|
||||
|
||||
onSubmit = (data) => {
|
||||
const { user_id, id } = data;
|
||||
return globalCredentialStore.client.delete(user_id, id);
|
||||
};
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// Copyright 2021 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 Create from './Create';
|
||||
import Delete from './Delete';
|
||||
|
||||
export const actionConfigs = {
|
||||
rowActions: {
|
||||
firstAction: Delete,
|
||||
},
|
||||
batchActions: [Delete],
|
||||
primaryActions: [Create],
|
||||
};
|
||||
|
||||
export const detailConfigs = {
|
||||
rowActions: {
|
||||
firstAction: Delete,
|
||||
},
|
||||
batchActions: [Delete],
|
||||
primaryActions: [],
|
||||
};
|
125
src/pages/user-center/containers/Credentials/index.jsx
Normal file
125
src/pages/user-center/containers/Credentials/index.jsx
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright 2021 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 React from 'react';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { Popover, Row, Col } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import Base from 'containers/List';
|
||||
import { CredentialStore } from 'stores/keystone/credential';
|
||||
import globalRootStore from 'stores/root';
|
||||
import { actionConfigs, detailConfigs } from './actions';
|
||||
|
||||
@inject('rootStore')
|
||||
@observer
|
||||
export default class Credentials extends Base {
|
||||
init() {
|
||||
this.store = new CredentialStore();
|
||||
this.downloadStore = new CredentialStore();
|
||||
}
|
||||
|
||||
get isUserDetail() {
|
||||
const {
|
||||
match: { path },
|
||||
} = this.props;
|
||||
return path.indexOf('user-admin/detail') >= 0;
|
||||
}
|
||||
|
||||
updateFetchParamsByPage = (params) => {
|
||||
if (!this.isUserDetail) {
|
||||
params.id = globalRootStore.user.user.id;
|
||||
}
|
||||
return params;
|
||||
};
|
||||
|
||||
get isFilterByBackend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get isSortByBackend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'identity:get_application_credential';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('application credential');
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
if (this.isUserDetail) {
|
||||
return detailConfigs;
|
||||
}
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
getColumns = () => {
|
||||
const ret = [
|
||||
{
|
||||
title: t('ID/Name'),
|
||||
dataIndex: 'name',
|
||||
hasNoDetail: true,
|
||||
},
|
||||
{
|
||||
title: t('Project ID/Name'),
|
||||
dataIndex: 'project_name',
|
||||
},
|
||||
{
|
||||
title: t('Description'),
|
||||
dataIndex: 'description',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Expires At'),
|
||||
dataIndex: 'expires_at',
|
||||
valueRender: 'toLocalTime',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Roles'),
|
||||
dataIndex: 'roles',
|
||||
render: (roles) => {
|
||||
const content = (
|
||||
<Row gutter={[8]} style={{ maxWidth: 200 }}>
|
||||
{roles.map((i) => (
|
||||
<Col span={24} key={i.id}>
|
||||
{i.name}
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
);
|
||||
return (
|
||||
<Popover content={content}>
|
||||
<SearchOutlined />
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
isHideable: true,
|
||||
},
|
||||
];
|
||||
return ret;
|
||||
};
|
||||
|
||||
get searchFilters() {
|
||||
const filters = [
|
||||
{
|
||||
label: t('Name'),
|
||||
name: 'name',
|
||||
},
|
||||
];
|
||||
return filters;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
import BaseLayout from 'layouts/Basic';
|
||||
|
||||
import E404 from 'pages/base/containers/404';
|
||||
import Credentials from '../containers/Credentials';
|
||||
import UserCenter from '../containers/UserCenter';
|
||||
|
||||
const PATH = '/user';
|
||||
@ -28,6 +29,11 @@ export default [
|
||||
component: UserCenter,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/application-credentials`,
|
||||
component: Credentials,
|
||||
exact: true,
|
||||
},
|
||||
{ path: '*', component: E404 },
|
||||
],
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const getOpenRc = (data) => {
|
||||
export const getPwdOpenRc = (data) => {
|
||||
const {
|
||||
authUrl,
|
||||
projectId,
|
||||
@ -36,7 +36,7 @@ export const getOpenRc = (data) => {
|
||||
'# OpenStack API is version 3. For example, your cloud provider may implement\n' +
|
||||
'# Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is\n' +
|
||||
'# only for the Identity API served through keystone.\n' +
|
||||
`export OS_AUTH_URL=${authUrl}\n` +
|
||||
`export OS_AUTH_URL=${authUrl}/v3/\n` +
|
||||
'\n' +
|
||||
'# With the addition of Keystone we have standardized on the term **project**\n' +
|
||||
'# as the entity that owns the resources.\n' +
|
||||
@ -70,3 +70,43 @@ export const getOpenRc = (data) => {
|
||||
|
||||
return openstackRc;
|
||||
};
|
||||
|
||||
export const getCredentialOpenRc = (data) => {
|
||||
const { authUrl, region } = data;
|
||||
const openstackRc =
|
||||
'#!/usr/bin/env bash\n' +
|
||||
'# To use an OpenStack cloud you need to authenticate against the Identity\n' +
|
||||
'# service named keystone, which returns a **Token** and **Service Catalog**.\n' +
|
||||
'# The catalog contains the endpoints for all services the user/tenant has\n' +
|
||||
'# access to - such as Compute, Image Service, Identity, Object Storage, Block\n' +
|
||||
'# Storage, and Networking (code-named nova, glance, keystone, swift,\n' +
|
||||
'# cinder, and neutron).\n' +
|
||||
'#\n' +
|
||||
'# *NOTE*: Using the 3 *Identity API* does not necessarily mean any other\n' +
|
||||
'# OpenStack API is version 3. For example, your cloud provider may implement\n' +
|
||||
'# Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is\n' +
|
||||
'# only for the Identity API served through keystone.\n' +
|
||||
`export OS_AUTH_URL=${authUrl}/v3/\n` +
|
||||
'\n' +
|
||||
'# With Keystone you pass the keystone password.\n' +
|
||||
'echo "Please enter your OpenStack Credential ID as OS_APPLICATION_CREDENTIAL_ID: "\n' +
|
||||
'read -sr OS_APPLICATION_CREDENTIAL_ID\n' +
|
||||
'export OS_APPLICATION_CREDENTIAL_ID=$OS_APPLICATION_CREDENTIAL_ID\n' +
|
||||
'echo "Please enter your OpenStack Credential Secret as OS_APPLICATION_CREDENTIAL_SECRET: "\n' +
|
||||
'read -sr OS_APPLICATION_CREDENTIAL_SECRET\n' +
|
||||
'export OS_APPLICATION_CREDENTIAL_SECRET=$OS_APPLICATION_CREDENTIAL_SECRET\n' +
|
||||
'\n' +
|
||||
"# Don't leave a blank variable, unset it if it was empty\n" +
|
||||
'if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi\n' +
|
||||
'export OS_INTERFACE=public\n' +
|
||||
'export OS_IDENTITY_API_VERSION=3\n' +
|
||||
'export OS_AUTH_TYPE=v3applicationcredential\n' +
|
||||
'# If your configuration has multiple regions, we set that information here.\n' +
|
||||
'# OS_REGION_NAME is optional and only valid in certain environments.\n' +
|
||||
`export OS_REGION_NAME=${region}\n` +
|
||||
'\n' +
|
||||
'# If OS_AUTH_URL use private SSL, Please add CACERT file path \n' +
|
||||
'# export OS_CACERT={crtPath}';
|
||||
|
||||
return openstackRc;
|
||||
};
|
||||
|
@ -16,8 +16,8 @@ const rolePermission = {
|
||||
project_admin: t('Project Admin'),
|
||||
project_reader: t('Project Reader'),
|
||||
project_member: t('Project Member'),
|
||||
system_admin: t('Syetem Admin'),
|
||||
system_reader: t('Syetem Reader'),
|
||||
system_admin: t('System Admin'),
|
||||
system_reader: t('System Reader'),
|
||||
};
|
||||
|
||||
export default rolePermission;
|
||||
|
66
src/stores/keystone/credential.js
Normal file
66
src/stores/keystone/credential.js
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2021 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 { action } from 'mobx';
|
||||
import client from 'client';
|
||||
import globalRootStore from 'stores/root';
|
||||
import globalUserStore from 'stores/keystone/user';
|
||||
import Base from '../base';
|
||||
|
||||
export class CredentialStore extends Base {
|
||||
get isSubResource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get client() {
|
||||
return client.keystone.users.applicationCredentials;
|
||||
}
|
||||
|
||||
get paramsFuncPage() {
|
||||
return (params) => {
|
||||
const { current, withPrice, id, ...rest } = params;
|
||||
return rest;
|
||||
};
|
||||
}
|
||||
|
||||
@action
|
||||
create(data) {
|
||||
const body = {};
|
||||
body[this.responseKey] = data;
|
||||
return this.submitting(
|
||||
this.client.create(globalRootStore.user.user.id, body)
|
||||
);
|
||||
}
|
||||
|
||||
async listDidFetch(items, allProjects) {
|
||||
if (!allProjects) {
|
||||
try {
|
||||
const results = await globalUserStore.getUserProjects();
|
||||
const projectNameMap = new Map();
|
||||
results.forEach((p) => {
|
||||
projectNameMap.set(p.id, p.name);
|
||||
});
|
||||
items.forEach((item) => {
|
||||
item.project_name = projectNameMap.get(item.project_id) || '-';
|
||||
});
|
||||
} catch (e) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
const globalCredentialStore = new CredentialStore();
|
||||
export default globalCredentialStore;
|
@ -151,6 +151,7 @@ export class UserStore extends Base {
|
||||
data: projects,
|
||||
isLoading: false,
|
||||
});
|
||||
return projects;
|
||||
}
|
||||
|
||||
@action
|
||||
|
Loading…
Reference in New Issue
Block a user