diff --git a/releasenotes/notes/Support-SSO-Login-via-OpenID-8ed955e35ebb1f1d.yaml b/releasenotes/notes/Support-SSO-Login-via-OpenID-8ed955e35ebb1f1d.yaml new file mode 100644 index 00000000..6c26cdeb --- /dev/null +++ b/releasenotes/notes/Support-SSO-Login-via-OpenID-8ed955e35ebb1f1d.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Support SSO login via OpenID connect. + + 1. If Web SSO login is successfully configured on the back-end, you can + select a login mode on the login page based on the configuration. The + default login mode is Keystone authentication. If you select the Web SSO + login mode, click Login button will switch to the Web SSO configuration page. + + 2. If SSO is not configured on the back-end, the front-end will directly + display the user + password login mode (Keystone authentication). You do + not need to select the login mode. diff --git a/src/client/skyline/index.js b/src/client/skyline/index.js index 81cf39de..04568f66 100644 --- a/src/client/skyline/index.js +++ b/src/client/skyline/index.js @@ -126,6 +126,9 @@ export class SkylineClient extends Base { name: 'queryRange', key: 'query_range', }, + { + key: 'sso', + }, ]; } } diff --git a/src/components/SimpleForm/index.jsx b/src/components/SimpleForm/index.jsx index 909fb84e..c684ea5b 100644 --- a/src/components/SimpleForm/index.jsx +++ b/src/components/SimpleForm/index.jsx @@ -65,13 +65,23 @@ export default class index extends Component { const { formItems } = this.props; // eslint-disable-next-line no-shadow return formItems.map((it, index) => { - const { name, hidden, dependencies = [], className, onChange } = it; + const { + name, + hidden, + dependencies = [], + className, + onChange, + extra, + label, + } = it; const options = { name, rules: this.getFormItemRules(it), hidden, dependencies, className, + extra, + label, }; if (onChange) { options.onChange = onChange; diff --git a/src/locales/en.json b/src/locales/en.json index 4706f59e..913a7f34 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1131,6 +1131,7 @@ "If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.", "If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).", "If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project", + "If you are not sure which authentication method to use, please contact your administrator.": "If you are not sure which authentication method to use, please contact your administrator.", "If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.": "If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.", "If you do not fill in parameters such as cpus, memory_mb, local_gb, cpu_arch, etc., you can automatically inject the configuration and Mac address of the physical machine by performing the \"Auto Inspect\" operation.": "If you do not fill in parameters such as cpus, memory_mb, local_gb, cpu_arch, etc., you can automatically inject the configuration and Mac address of the physical machine by performing the \"Auto Inspect\" operation.", "If you still want to keep the disk data, it is recommended that you create a snapshot for the disk before deleting.": "If you still want to keep the disk data, it is recommended that you create a snapshot for the disk before deleting.", @@ -1304,6 +1305,7 @@ "Keypair": "Keypair", "Keypair Detail": "Keypair Detail", "Keypair Info": "Keypair Info", + "Keystone Credentials": "Keystone Credentials", "Keystone token is expired.": "token has expired, please check whether the server time is correct and confirm whether the token is valid", "Kill": "Kill", "Kill Container": "Kill Container", @@ -1613,6 +1615,7 @@ "Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field": "Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field", "Only subnets that are already connected to the router can be selected.": "Only subnets that are already connected to the router can be selected.", "Open External Gateway": "Open External Gateway", + "OpenID Connect": "OpenID Connect", "OpenStack Services": "OpenStack Services", "Operating Status": "Operating Status", "Operating System": "Operating System", @@ -1756,6 +1759,7 @@ "Please select availability zone": "Please select availability zone", "Please select item!": "Please select item!", "Please select key": "Please select key", + "Please select login type!": "Please select login type!", "Please select policy": "Please select policy", "Please select source": "Please select source", "Please select type": "Please select type", @@ -2037,6 +2041,7 @@ "Select User Group": "Select User Group", "Select Volume Snapshot": "Select Volume Snapshot", "Select a domain": "Select a domain", + "Select a login type": "Select a login type", "Select a region": "Select a region", "Selected": "Selected", "Selected Members": "Selected Members", diff --git a/src/locales/zh.json b/src/locales/zh.json index fb1fb607..5d81cf12 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1131,6 +1131,7 @@ "If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "如果云硬盘容量较大,修改云硬盘类型可能需要花费几个小时,请您谨慎操作。", "If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "若快照关联的云硬盘修改过云硬盘类型,请手动修改此选项;若快照关联的云硬盘保持云硬盘类型不变,请忽略此选项(不需要做变更)。", "If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "您未被授权访问任何项目,或您参与中的项目已被删除或禁用,可联系平台管理员重新分配项目", + "If you are not sure which authentication method to use, please contact your administrator.": "如果您不确定使用哪种认证方式,请联系管理员。", "If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.": "如果你选择了和LB子网不同的网卡,请确保两者的连通性。", "If you do not fill in parameters such as cpus, memory_mb, local_gb, cpu_arch, etc., you can automatically inject the configuration and Mac address of the physical machine by performing the \"Auto Inspect\" operation.": "如不填写cpus、memory_mb、local_gb、cpu_arch等参数,您可以通过执行“自动检测”操作来自动注入物理机的配置和 Mac 地址。", "If you still want to keep the disk data, it is recommended that you create a snapshot for the disk before deleting.": "如果您仍想保留云硬盘数据,建议您在删除之前为云硬盘创建快照。", @@ -1304,6 +1305,7 @@ "Keypair": "SSH密钥对", "Keypair Detail": "密钥详情", "Keypair Info": "密钥信息", + "Keystone Credentials": "Keystone认证", "Keystone token is expired.": "token已过期,请检查服务器时间是否正确,确认token是否有效", "Kill": "终止", "Kill Container": "终止容器", @@ -1613,6 +1615,7 @@ "Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field": "只可填写交换机的Mac地址或者交换机基于openflow的数据路径ID", "Only subnets that are already connected to the router can be selected.": "仅可选择已经连接过路由器的子网。", "Open External Gateway": "开启公网网关", + "OpenID Connect": "OpenID连接", "OpenStack Services": "OpenStack服务", "Operating Status": "操作状态", "Operating System": "操作系统", @@ -1756,6 +1759,7 @@ "Please select availability zone": "请选择可用域", "Please select item!": "请选择一个条目!", "Please select key": "请选择一个键", + "Please select login type!": "请选择登录方式!", "Please select policy": "请选择一个策略", "Please select source": "请选择源", "Please select type": "请选择类型", @@ -2037,6 +2041,7 @@ "Select User Group": "选择用户组", "Select Volume Snapshot": "选择云硬盘快照", "Select a domain": "请选择Domain", + "Select a login type": "请选择登录方式", "Select a region": "请选择Region", "Selected": "已选", "Selected Members": "已选择成员", diff --git a/src/pages/auth/containers/Login/index.jsx b/src/pages/auth/containers/Login/index.jsx index c4cbdd2f..dbee231b 100644 --- a/src/pages/auth/containers/Login/index.jsx +++ b/src/pages/auth/containers/Login/index.jsx @@ -31,12 +31,14 @@ export class Login extends Component { error: false, message: '', loading: false, + loginTypeOption: this.passwordOption, }; } componentDidMount() { this.getDomains(); this.getRegions(); + this.getSSO(); } async getDomains() { @@ -49,6 +51,14 @@ export class Login extends Component { this.updateDefaultValue(); } + async getSSO() { + try { + this.store.fetchSSO(); + } catch (e) { + console.log(e); + } + } + get rootStore() { return this.props.rootStore; } @@ -89,8 +99,67 @@ export class Login extends Component { return '/base/overview'; } + get enableSSO() { + const { sso: { enable_sso = false } = {} } = this.store; + return enable_sso; + } + + get ssoProtocols() { + return { + openid: t('OpenID Connect'), + }; + } + + get SSOOptions() { + if (!this.enableSSO) { + return []; + } + const { sso: { protocols = [] } = {} } = this.store; + return protocols.map((it) => { + const { protocol, url } = it; + return { + label: this.ssoProtocols[protocol] || protocol, + value: url, + ...it, + }; + }); + } + + get passwordOption() { + return { + label: t('Keystone Credentials'), + value: 'password', + }; + } + + get loginTypeOptions() { + if (!this.enableSSO) { + return []; + } + return [this.passwordOption, ...this.SSOOptions]; + } + + onLoginTypeChange = (value, option) => { + this.setState({ loginTypeOption: option }); + }; + + get currentLoginType() { + const { loginTypeOption: { value } = {} } = this.state; + if (value === 'password') { + return 'password'; + } + return 'sso'; + } + + get currentSSOLink() { + const { loginTypeOption: { value } = {} } = this.state; + return value; + } + get defaultValue() { - const data = {}; + const data = { + loginType: 'password', + }; if (this.regions.length === 1) { data.region = this.regions[0].value; } @@ -107,84 +176,150 @@ export class Login extends Component { block: true, type: 'primary', }; - return [ - { - name: 'error', - hidden: !error, - render: () => ( -