feat(X-Pack): Add OIDC authentication mechanism

This commit is contained in:
fit2cloud-chenyw
2025-11-27 15:36:56 +08:00
committed by fit2cloud-chenyw
parent 8023ce2907
commit 0280fe6d11
16 changed files with 345 additions and 89 deletions
+5 -5
View File
@@ -1,14 +1,14 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="32" height="32" viewBox="0 0 32 32" fill="" xmlns="http://www.w3.org/2000/svg">
<g id="logo_cas">
<g id="logo_cas_2">
<mask id="mask0_3028_68898" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="1" width="30" height="30">
<circle id="Ellipse 130" cx="16" cy="16" r="15" fill="white"/>
<circle id="Ellipse 130" cx="16" cy="16" r="15" fill=""/>
</mask>
<g mask="url(#mask0_3028_68898)">
<g id="CAS">
<path d="M8.38996 11.992C9.30296 11.992 10.062 12.223 10.656 12.707C11.228 13.169 11.58 13.807 11.701 14.599H10.535C10.403 14.071 10.15 13.675 9.77596 13.422C9.40196 13.169 8.93996 13.048 8.36796 13.048C7.52096 13.048 6.88296 13.334 6.45396 13.917C6.04696 14.445 5.84896 15.171 5.84896 16.084C5.84896 17.03 6.04696 17.756 6.44296 18.273C6.86096 18.823 7.50996 19.098 8.38996 19.098C8.96196 19.098 9.44596 18.955 9.81996 18.669C10.216 18.361 10.491 17.899 10.645 17.283H11.811C11.635 18.196 11.228 18.911 10.579 19.428C9.97396 19.912 9.24796 20.154 8.40096 20.154C7.11396 20.154 6.14596 19.736 5.49696 18.922C4.92496 18.218 4.64996 17.272 4.64996 16.084C4.64996 14.918 4.93596 13.972 5.52996 13.235C6.18996 12.399 7.13596 11.992 8.38996 11.992Z" fill="#3370FF"/>
<path d="M15.2809 12.146H16.6449L19.6699 20H18.3829L17.6459 17.976H14.2689L13.5319 20H12.2559L15.2809 12.146ZM14.6319 16.986H17.2829L15.9849 13.433H15.9409L14.6319 16.986Z" fill="#3370FF"/>
<path d="M23.188 11.992C24.101 11.992 24.816 12.179 25.333 12.575C25.883 12.982 26.191 13.609 26.268 14.467H25.08C24.97 13.961 24.772 13.587 24.464 13.367C24.156 13.136 23.716 13.026 23.122 13.026C22.605 13.026 22.209 13.103 21.934 13.257C21.593 13.433 21.428 13.719 21.428 14.115C21.428 14.467 21.615 14.742 22.011 14.951C22.187 15.05 22.638 15.215 23.375 15.435C24.431 15.765 25.124 16.018 25.432 16.205C26.103 16.612 26.444 17.173 26.444 17.899C26.444 18.603 26.169 19.153 25.619 19.56C25.069 19.956 24.299 20.154 23.309 20.154C22.352 20.154 21.604 19.956 21.065 19.582C20.416 19.12 20.064 18.394 19.998 17.404H21.186C21.274 18.02 21.494 18.471 21.846 18.735C22.165 18.977 22.649 19.098 23.309 19.098C23.903 19.098 24.376 18.988 24.728 18.79C25.08 18.592 25.256 18.317 25.256 17.965C25.256 17.525 25.003 17.184 24.508 16.931C24.332 16.843 23.815 16.667 22.946 16.403C21.978 16.095 21.384 15.886 21.142 15.754C20.537 15.391 20.24 14.863 20.24 14.181C20.24 13.488 20.526 12.949 21.109 12.553C21.659 12.179 22.352 11.992 23.188 11.992Z" fill="#3370FF"/>
<path d="M8.38996 11.992C9.30296 11.992 10.062 12.223 10.656 12.707C11.228 13.169 11.58 13.807 11.701 14.599H10.535C10.403 14.071 10.15 13.675 9.77596 13.422C9.40196 13.169 8.93996 13.048 8.36796 13.048C7.52096 13.048 6.88296 13.334 6.45396 13.917C6.04696 14.445 5.84896 15.171 5.84896 16.084C5.84896 17.03 6.04696 17.756 6.44296 18.273C6.86096 18.823 7.50996 19.098 8.38996 19.098C8.96196 19.098 9.44596 18.955 9.81996 18.669C10.216 18.361 10.491 17.899 10.645 17.283H11.811C11.635 18.196 11.228 18.911 10.579 19.428C9.97396 19.912 9.24796 20.154 8.40096 20.154C7.11396 20.154 6.14596 19.736 5.49696 18.922C4.92496 18.218 4.64996 17.272 4.64996 16.084C4.64996 14.918 4.93596 13.972 5.52996 13.235C6.18996 12.399 7.13596 11.992 8.38996 11.992Z" fill=""/>
<path d="M15.2809 12.146H16.6449L19.6699 20H18.3829L17.6459 17.976H14.2689L13.5319 20H12.2559L15.2809 12.146ZM14.6319 16.986H17.2829L15.9849 13.433H15.9409L14.6319 16.986Z" fill=""/>
<path d="M23.188 11.992C24.101 11.992 24.816 12.179 25.333 12.575C25.883 12.982 26.191 13.609 26.268 14.467H25.08C24.97 13.961 24.772 13.587 24.464 13.367C24.156 13.136 23.716 13.026 23.122 13.026C22.605 13.026 22.209 13.103 21.934 13.257C21.593 13.433 21.428 13.719 21.428 14.115C21.428 14.467 21.615 14.742 22.011 14.951C22.187 15.05 22.638 15.215 23.375 15.435C24.431 15.765 25.124 16.018 25.432 16.205C26.103 16.612 26.444 17.173 26.444 17.899C26.444 18.603 26.169 19.153 25.619 19.56C25.069 19.956 24.299 20.154 23.309 20.154C22.352 20.154 21.604 19.956 21.065 19.582C20.416 19.12 20.064 18.394 19.998 17.404H21.186C21.274 18.02 21.494 18.471 21.846 18.735C22.165 18.977 22.649 19.098 23.309 19.098C23.903 19.098 24.376 18.988 24.728 18.79C25.08 18.592 25.256 18.317 25.256 17.965C25.256 17.525 25.003 17.184 24.508 16.931C24.332 16.843 23.815 16.667 22.946 16.403C21.978 16.095 21.384 15.886 21.142 15.754C20.537 15.391 20.24 14.863 20.24 14.181C20.24 13.488 20.526 12.949 21.109 12.553C21.659 12.179 22.352 11.992 23.188 11.992Z" fill=""/>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

+2 -2
View File
@@ -1,3 +1,3 @@
<svg width="27" height="8" viewBox="0 0 27 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.66736 0.448C4.63936 0.448 5.41336 0.763 5.98036 1.402C6.52036 2.005 6.79036 2.806 6.79036 3.796C6.79036 4.777 6.52036 5.569 5.98036 6.181C5.41336 6.811 4.63936 7.126 3.66736 7.126C2.68636 7.126 1.91236 6.802 1.35436 6.172C0.814359 5.56 0.553359 4.768 0.553359 3.796C0.553359 2.815 0.814359 2.023 1.35436 1.411C1.91236 0.763 2.68636 0.448 3.66736 0.448ZM3.66736 1.312C2.98336 1.312 2.45236 1.537 2.07436 2.005C1.71436 2.446 1.53436 3.04 1.53436 3.796C1.53436 4.543 1.71436 5.137 2.07436 5.578C2.45236 6.028 2.98336 6.262 3.66736 6.262C4.35136 6.262 4.88236 6.037 5.25136 5.596C5.61136 5.164 5.80036 4.561 5.80036 3.796C5.80036 3.022 5.61136 2.419 5.25136 1.978C4.88236 1.528 4.35136 1.312 3.66736 1.312ZM9.6293 0.574H10.7453L13.2203 7H12.1673L11.5643 5.344H8.8013L8.1983 7H7.1543L9.6293 0.574ZM9.0983 4.534H11.2673L10.2053 1.627H10.1693L9.0983 4.534ZM13.7588 2.347H14.7128V5.182C14.7128 5.578 14.7938 5.866 14.9648 6.046C15.1268 6.226 15.3968 6.325 15.7658 6.325C16.0178 6.325 16.2428 6.217 16.4588 6.019C16.6748 5.812 16.8278 5.533 16.9088 5.191V2.347H17.8628V7H16.9088V6.37C16.5128 6.874 16.0268 7.126 15.4418 7.126C14.3168 7.126 13.7588 6.496 13.7588 5.245V2.347ZM20.3661 0.844V2.347H21.4281V3.139H20.3661V5.839C20.3661 5.965 20.3931 6.055 20.4471 6.118C20.5011 6.172 20.5821 6.208 20.6991 6.208H21.3111V7H20.5461C20.1501 7 19.8531 6.892 19.6731 6.685C19.4931 6.487 19.4121 6.208 19.4121 5.839V3.139H18.5481V2.347H19.4121V1.24L20.3661 0.844ZM22.1963 0.448H23.1503V3.004C23.3213 2.734 23.5373 2.527 23.7983 2.401C24.0322 2.275 24.2933 2.221 24.5903 2.221C25.1483 2.221 25.5713 2.383 25.8593 2.716C26.1293 3.031 26.2643 3.499 26.2643 4.111V7H25.3103V4.273C25.3103 3.859 25.2203 3.553 25.0583 3.337C24.8783 3.112 24.5993 3.004 24.2393 3.004C23.9153 3.004 23.6543 3.13 23.4563 3.391C23.2493 3.652 23.1503 3.985 23.1503 4.39V7H22.1963V0.448Z" fill="#3370FF"/>
<svg width="27" height="8" viewBox="0 0 27 8" fill="" xmlns="http://www.w3.org/2000/svg">
<path d="M3.66736 0.448C4.63936 0.448 5.41336 0.763 5.98036 1.402C6.52036 2.005 6.79036 2.806 6.79036 3.796C6.79036 4.777 6.52036 5.569 5.98036 6.181C5.41336 6.811 4.63936 7.126 3.66736 7.126C2.68636 7.126 1.91236 6.802 1.35436 6.172C0.814359 5.56 0.553359 4.768 0.553359 3.796C0.553359 2.815 0.814359 2.023 1.35436 1.411C1.91236 0.763 2.68636 0.448 3.66736 0.448ZM3.66736 1.312C2.98336 1.312 2.45236 1.537 2.07436 2.005C1.71436 2.446 1.53436 3.04 1.53436 3.796C1.53436 4.543 1.71436 5.137 2.07436 5.578C2.45236 6.028 2.98336 6.262 3.66736 6.262C4.35136 6.262 4.88236 6.037 5.25136 5.596C5.61136 5.164 5.80036 4.561 5.80036 3.796C5.80036 3.022 5.61136 2.419 5.25136 1.978C4.88236 1.528 4.35136 1.312 3.66736 1.312ZM9.6293 0.574H10.7453L13.2203 7H12.1673L11.5643 5.344H8.8013L8.1983 7H7.1543L9.6293 0.574ZM9.0983 4.534H11.2673L10.2053 1.627H10.1693L9.0983 4.534ZM13.7588 2.347H14.7128V5.182C14.7128 5.578 14.7938 5.866 14.9648 6.046C15.1268 6.226 15.3968 6.325 15.7658 6.325C16.0178 6.325 16.2428 6.217 16.4588 6.019C16.6748 5.812 16.8278 5.533 16.9088 5.191V2.347H17.8628V7H16.9088V6.37C16.5128 6.874 16.0268 7.126 15.4418 7.126C14.3168 7.126 13.7588 6.496 13.7588 5.245V2.347ZM20.3661 0.844V2.347H21.4281V3.139H20.3661V5.839C20.3661 5.965 20.3931 6.055 20.4471 6.118C20.5011 6.172 20.5821 6.208 20.6991 6.208H21.3111V7H20.5461C20.1501 7 19.8531 6.892 19.6731 6.685C19.4931 6.487 19.4121 6.208 19.4121 5.839V3.139H18.5481V2.347H19.4121V1.24L20.3661 0.844ZM22.1963 0.448H23.1503V3.004C23.3213 2.734 23.5373 2.527 23.7983 2.401C24.0322 2.275 24.2933 2.221 24.5903 2.221C25.1483 2.221 25.5713 2.383 25.8593 2.716C26.1293 3.031 26.2643 3.499 26.2643 4.111V7H25.3103V4.273C25.3103 3.859 25.2203 3.553 25.0583 3.337C24.8783 3.112 24.5993 3.004 24.2393 3.004C23.9153 3.004 23.6543 3.13 23.4563 3.391C23.2493 3.652 23.1503 3.985 23.1503 4.39V7H22.1963V0.448Z" fill=""/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+17
View File
@@ -0,0 +1,17 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_oidc">
<g id="logo_oidc">
<mask id="mask0_3028_68876" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="1" width="30" height="30">
<circle id="Ellipse 130" cx="16" cy="16" r="15" fill="white"/>
</mask>
<g mask="url(#mask0_3028_68876)">
<g id="OIDC">
<path d="M6.59294 11.992C7.79194 11.992 8.73794 12.377 9.43094 13.158C10.0909 13.895 10.4209 14.874 10.4209 16.084C10.4209 17.294 10.0909 18.262 9.43094 18.999C8.73794 19.769 7.79194 20.154 6.59294 20.154C5.38294 20.154 4.43694 19.758 3.75494 18.988C3.09494 18.24 2.77594 17.272 2.77594 16.084C2.77594 14.885 3.09494 13.917 3.75494 13.169C4.43694 12.377 5.38294 11.992 6.59294 11.992ZM6.59294 13.125C5.77894 13.125 5.15194 13.4 4.70094 13.95C4.27194 14.478 4.06294 15.182 4.06294 16.084C4.06294 16.975 4.27194 17.679 4.70094 18.207C5.14094 18.746 5.77894 19.021 6.59294 19.021C7.40694 19.021 8.03394 18.757 8.47394 18.24C8.90294 17.723 9.12294 17.008 9.12294 16.084C9.12294 15.16 8.90294 14.434 8.47394 13.906C8.03394 13.378 7.40694 13.125 6.59294 13.125Z" class="svg-primary-color"/>
<path d="M11.6352 12.146H12.9222V20H11.6352V12.146Z" class="svg-primary-color"/>
<path d="M14.4604 12.146H17.3314C18.6074 12.146 19.5644 12.498 20.2244 13.202C20.8514 13.862 21.1704 14.819 21.1704 16.073C21.1704 17.316 20.8514 18.273 20.2244 18.944C19.5644 19.648 18.6074 20 17.3314 20H14.4604V12.146ZM15.7474 13.246V18.9H17.0894C18.0684 18.9 18.7834 18.669 19.2344 18.218C19.6744 17.756 19.8944 17.041 19.8944 16.073C19.8944 15.083 19.6744 14.357 19.2344 13.917C18.7834 13.466 18.0684 13.246 17.0894 13.246H15.7474Z" class="svg-primary-color"/>
<path d="M25.7874 11.992C26.7114 11.992 27.4814 12.234 28.0754 12.718C28.6474 13.18 28.9884 13.818 29.1094 14.61H27.8554C27.7234 14.093 27.4814 13.719 27.1184 13.477C26.7664 13.235 26.3154 13.125 25.7654 13.125C24.9404 13.125 24.3244 13.4 23.9064 13.972C23.5214 14.478 23.3344 15.182 23.3344 16.084C23.3344 17.008 23.5214 17.723 23.8954 18.218C24.3024 18.746 24.9404 19.021 25.7984 19.021C26.3594 19.021 26.8214 18.878 27.1734 18.614C27.5474 18.317 27.8114 17.866 27.9654 17.272H29.2194C29.0434 18.196 28.6364 18.911 27.9874 19.428C27.3824 19.912 26.6564 20.154 25.8094 20.154C24.5004 20.154 23.5214 19.736 22.8834 18.922C22.3224 18.218 22.0474 17.272 22.0474 16.084C22.0474 14.918 22.3334 13.961 22.9164 13.235C23.5764 12.399 24.5334 11.992 25.7874 11.992Z" class="svg-primary-color"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+3 -2
View File
@@ -83,8 +83,9 @@ const savePwdHandler = () => {
pwdFormRef.value?.submit()
}
const logout = async () => {
await userStore.logout()
router.push('/login')
if (!(await userStore.logout())) {
router.push('/login')
}
}
</script>
+3 -2
View File
@@ -236,8 +236,9 @@ const menuSelect = (e: any) => {
router.push(e.index)
}
const logout = async () => {
await userStore.logout()
router.push('/login')
if (!(await userStore.logout())) {
router.push('/login')
}
}
const toSystem = () => {
router.push('/system')
+7 -2
View File
@@ -737,7 +737,11 @@
"revoke_url": "Revocation URL",
"oauth2_field_mapping_placeholder": "Example: {'{'}\"account\": \"OAuth2Account\", \"name\": \"OAuth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "Token auth method",
"userinfo_auth_method": "Userinfo auth method"
"userinfo_auth_method": "Userinfo auth method",
"oidc_settings": "OIDC Settings",
"metadata_url": "Metadata URL",
"realm": "Realm",
"oidc_field_mapping_placeholder": "e.g., {\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"}"
},
"login": {
"default_login": "Default",
@@ -749,7 +753,8 @@
"qr_code": "QR Code",
"platform_disable": "{0} settings are not enabled!",
"input_account": "Please enter account",
"redirect_2_auth": "Redirecting to {0} authentication, {1} seconds..."
"redirect_2_auth": "Redirecting to {0} authentication, {1} seconds...",
"redirect_immediately": "Redirecting immediately"
},
"supplier": {
"alibaba_cloud_bailian": "Alibaba Cloud Bailian",
+7 -2
View File
@@ -737,7 +737,11 @@
"revoke_url": "취소 URL",
"oauth2_field_mapping_placeholder": "예: {'{'}\"account\": \"OAuth2Account\", \"name\": \"OAuth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "토큰 인증 방식",
"userinfo_auth_method": "사용자 정보 인증 방식"
"userinfo_auth_method": "사용자 정보 인증 방식",
"oidc_settings": "OIDC 설정",
"metadata_url": "메타데이터 URL",
"realm": "영역",
"oidc_field_mapping_placeholder": "예: {\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"}"
},
"login": {
"default_login": "기본값",
@@ -749,7 +753,8 @@
"qr_code": "QR 코드",
"platform_disable": "{0} 설정이 활성화되지 않았습니다!",
"input_account": "계정을 입력해 주세요",
"redirect_2_auth": "{0} 인증으로 리디렉션 중입니다, {1}초..."
"redirect_2_auth": "{0} 인증으로 리디렉션 중입니다, {1}초...",
"redirect_immediately": "지금 이동"
},
"supplier": {
"alibaba_cloud_bailian": "알리바바 클라우드 바이리엔",
+7 -2
View File
@@ -737,7 +737,11 @@
"revoke_url": "撤销地址",
"oauth2_field_mapping_placeholder": "例如:{'{'}\"account\": \"oauth2Account\", \"name\": \"oauth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "Token 认证方式",
"userinfo_auth_method": "用户信息认证方式"
"userinfo_auth_method": "用户信息认证方式",
"oidc_settings": "OIDC 设置",
"metadata_url": "元数据地址",
"realm": "领域",
"oidc_field_mapping_placeholder": "例如:{'{'}\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"{'}'}"
},
"login": {
"default_login": "默认",
@@ -749,7 +753,8 @@
"qr_code": "二维码",
"platform_disable": "{0}设置未开启!",
"input_account": "请输入账号",
"redirect_2_auth": "正在跳转至 {0} 认证,{1} 秒..."
"redirect_2_auth": "正在跳转至 {0} 认证,{1} 秒...",
"redirect_immediately": "立即跳转"
},
"supplier": {
"alibaba_cloud_bailian": "阿里云百炼",
+3
View File
@@ -91,12 +91,15 @@ export const UserStore = defineStore('user', {
if (res) {
window.location.href = res
window.open(res, '_self')
return res
}
if (getQueryString('code') && getQueryString('state')?.includes('oauth2_state')) {
const logout_url = location.origin + location.pathname + '#/login'
window.location.href = logout_url
window.open(res, logout_url)
return logout_url
}
return null
},
async info() {
+3 -2
View File
@@ -18,8 +18,9 @@ const router = useRouter()
const userStore = useUserStore()
const logout = async () => {
await userStore.logout()
router.push('/login')
if (!(await userStore.logout())) {
router.push('/login')
}
}
</script>
+2 -1
View File
@@ -31,6 +31,7 @@ const execute = () => {
font-size: 32px;
border: 1px solid #dee0e3;
border-radius: 50%;
color: var(--ed-color-primary);
}
display: flex;
align-items: center;
@@ -41,7 +42,7 @@ const execute = () => {
margin-top: 8px;
color: #000;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-family: var(--ed-color-primary, 'PingFang');
font-size: 12px;
font-style: normal;
font-weight: 400;
+167 -5
View File
@@ -19,8 +19,8 @@
:qrcode="loginCategory.qrcode"
:ldap="loginCategory.ldap"
@status-change="qrStatusChange"
/>
<Oidc v-if="loginCategory.oidc" @switch-category="switcherCategory" /> -->
/> -->
<Oidc v-if="loginCategory.oidc" @switch-category="switcherCategory" />
<Oauth2 v-if="loginCategory.oauth2" ref="oauth2Handler" @switch-category="switcherCategory" />
<Cas v-if="loginCategory.cas" @switch-category="switcherCategory" />
<!-- <Saml2 v-if="loginCategory.saml2" ref="saml2Handler" @switch-category="switcherCategory" /> -->
@@ -29,6 +29,24 @@
<!-- <mfa-step v-if="showMfa" :mfa-data="state.mfaData" @close="showMfa = false" />
<platform-error v-if="platformLoginMsg" :msg="platformLoginMsg" /> -->
<el-dialog
v-model="loginDialogVisible"
:title="dialogTitle"
width="420"
:destroy-on-close="true"
:close-on-click-modal="false"
modal-class="login-platform-dialog"
@closed="closeHandler"
>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeHandler">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="redirectImmediately">
{{ t('login.redirect_immediately') }}
</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
@@ -37,6 +55,7 @@ import { ref, onMounted } from 'vue'
import Oidc from './Oidc.vue'
import Oauth2 from './Oauth2.vue'
import Saml2 from './Saml2.vue' */
import Oidc from './Oidc.vue'
import Cas from './Cas.vue'
import Oauth2 from './Oauth2.vue'
// import QrTab from './QrTab.vue'
@@ -63,6 +82,17 @@ interface Categoryparam {
category: string
proxy?: string
}
const loginDialogVisible = ref(false)
const dialogTitle = ref('')
const dialogInterval = ref<any>(null)
const currentCancelHandler = ref<((reason?: string) => void) | null>(null)
const currentSureHandler = ref<(() => void) | null>(null)
interface RedirectDialogResult {
promise: Promise<boolean>
sure: () => void
cancel: (reason?: string) => void
}
const platformLoginMsg = ref('')
const { wsCache } = useCache()
const userStore = useUserStore()
@@ -78,6 +108,83 @@ const saml2Handler = ref()
ready: false,
},
}) */
const openDialog = (origin_text: string): RedirectDialogResult => {
const platforms: { [key: string]: string } = {
cas: 'CAS',
oidc: 'OIDC',
ldap: 'LDAP',
oauth2: 'OAuth2',
saml2: 'Saml2',
}
let timer = 3
const platFormName = platforms[origin_text] || 'SSO'
dialogTitle.value = t('login.redirect_2_auth', [platFormName, timer])
let rejectPromise: ((reason?: any) => void) | null = null
let resolvePromise: ((value: boolean | PromiseLike<boolean>) => void) | null = null
loginDialogVisible.value = true
const promise = new Promise<boolean>((resolve, reject) => {
rejectPromise = reject
resolvePromise = resolve
dialogInterval.value = setInterval(() => {
if (timer-- <= 0) {
clearInterval(dialogInterval.value)
closeDialog()
resolve(true)
return
}
dialogTitle.value = t('login.redirect_2_auth', [platFormName, timer])
}, 1000)
})
const sure = (): void => {
if (dialogInterval.value) {
clearInterval(dialogInterval.value)
dialogInterval.value = null
}
closeDialog()
if (resolvePromise) {
resolvePromise(true)
resolvePromise = null
}
}
const cancel = (reason: string = '用户取消跳转'): void => {
if (dialogInterval.value) {
clearInterval(dialogInterval.value)
dialogInterval.value = null
}
closeDialog()
if (rejectPromise) {
rejectPromise(new Error(reason))
rejectPromise = null
}
}
return {
promise,
sure,
cancel,
}
}
const closeHandler = () => {
if (currentCancelHandler.value) {
currentCancelHandler.value('手动取消')
}
}
const closeDialog = () => {
loginDialogVisible.value = false
/* if (loginDialogVisible.value) {
clearInterval(dialogInterval.value)
dialogInterval.value = null
} */
}
const redirectImmediately = () => {
if (currentSureHandler.value) {
currentSureHandler.value()
}
}
const init = (cb?: () => void) => {
queryCategoryStatus()
.then((res) => {
@@ -136,7 +243,7 @@ const toMfa = (mfa) => {
}
} */
const switcherCategory = (param: Categoryparam) => {
const switcherCategory = async (param: Categoryparam) => {
const { category, proxy } = param
const curOrigin = window.location.origin
const curLocation = getCurLocation()
@@ -145,6 +252,21 @@ const switcherCategory = (param: Categoryparam) => {
emits('switchTab', category || 'simple')
return
}
const { promise, sure, cancel } = openDialog(category)
currentCancelHandler.value = cancel
currentSureHandler.value = sure
let shouldRedirect = false
try {
shouldRedirect = await promise
} finally {
currentCancelHandler.value = null
currentSureHandler.value = null
}
if (!shouldRedirect) {
return
}
let pathname = window.location.pathname
if (pathname) {
pathname = pathname.substring(0, pathname.length - 1)
@@ -157,6 +279,13 @@ const switcherCategory = (param: Categoryparam) => {
})
return
}
if (category === 'oidc') {
request.get('/system/authentication/login/2').then((res: any) => {
window.location.href = res
window.open(res, '_self')
})
return
}
if (category === 'saml2') {
saml2Handler?.value?.toLoginPage()
return
@@ -182,8 +311,6 @@ const getCurLocation = () => {
const casLogin = () => {
const urlParams = getUrlParams()
const ticket = getQueryString('ticket')
/* request
.get('/system/authentication/sso/cas?ticket=' + ticket) */
request
.post('/system/authentication/sso/1', urlParams)
.then((res: any) => {
@@ -247,6 +374,39 @@ const oauth2Login = () => {
}, 1500)
})
}
const oidcLogin = () => {
const urlParams = getUrlParams()
request
.post('/system/authentication/sso/2', urlParams)
.then((res: any) => {
const token = res.access_token
const platform_info = res.platform_info
if (token && isPlatformClient()) {
wsCache.set('de-platform-client', true)
}
userStore.setToken(token)
userStore.setExp(res.exp)
userStore.setTime(Date.now())
userStore.setPlatformInfo({
flag: 'oidc',
data: platform_info ? JSON.stringify(platform_info) : '',
origin: 2,
})
const queryRedirectPath = getCurLocation()
router.push({ path: queryRedirectPath })
})
.catch((e: any) => {
userStore.setToken('')
setTimeout(() => {
// logoutHandler(true, true)
platformLoginMsg.value = e?.message || e
setTimeout(() => {
window.location.href =
window.location.origin + window.location.pathname + window.location.hash
}, 2000)
}, 1500)
})
}
/* const platformLogin = (origin: number) => {
const url = '/system/authentication/sso/cas'
request
@@ -426,6 +586,8 @@ onMounted(() => {
casLogin()
} else if (state?.includes('oauth2')) {
oauth2Login()
} else if (state?.includes('oidc')) {
oidcLogin()
} else {
updateLoading(false)
}
+2 -1
View File
@@ -31,6 +31,7 @@ const execute = () => {
font-size: 32px;
border: 1px solid #dee0e3;
border-radius: 50%;
color: var(--ed-color-primary);
}
display: flex;
align-items: center;
@@ -41,7 +42,7 @@ const execute = () => {
margin-top: 8px;
color: #000;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-family: var(--ed-color-primary, 'PingFang');
font-size: 12px;
font-style: normal;
font-weight: 400;
+53
View File
@@ -0,0 +1,53 @@
<template>
<div class="item OIDC" @click="execute">
<el-icon>
<Icon name="logo_oidc"><logo_oidc class="svg-icon" /></Icon>
</el-icon>
<span class="name"> OIDC </span>
</div>
</template>
<script lang="ts" setup>
import logo_oidc from '@/assets/svg/logo_oidc.svg'
import { Icon } from '@/components/icon-custom'
const emits = defineEmits(['switch-category'])
const execute = () => {
emits('switch-category', { category: 'oidc', proxy: '/#' })
}
</script>
<style lang="less" scoped>
.item {
width: 32px;
cursor: pointer;
&.qrcode,
&.account {
.ed-icon {
padding: 5px;
}
}
.ed-icon {
font-size: 32px;
border: 1px solid #dee0e3;
border-radius: 50%;
color: var(--ed-color-primary);
}
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
.name {
margin-top: 8px;
color: #000;
text-align: center;
font-family: var(--ed-color-primary, 'PingFang');
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
display: none;
}
}
</style>
@@ -8,25 +8,16 @@ const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
const oidcForm = ref<FormInstance>()
interface OidcForm {
clientId?: string
clientSecret?: string
discovery?: string
redirectUri?: string
realm?: string
scope?: string
usePkce?: boolean
mapping?: string
}
const id = ref<number | null>(null)
const state = reactive({
form: reactive<OidcForm>({
clientId: '',
clientSecret: '',
discovery: '',
redirectUri: '',
form: reactive<any>({
client_id: '',
client_secret: '',
metadata_url: '',
redirect_uri: '',
realm: '',
scope: '',
usePkce: false,
mapping: '',
}),
})
@@ -48,13 +39,14 @@ const validateMapping = (rule, value, callback) => {
}
try {
JSON.parse(value)
} catch (e) {
} catch (e: any) {
console.error(e)
callback(new Error(t('system.in_json_format')))
}
callback()
}
const rule = reactive<FormRules>({
clientId: [
client_id: [
{
required: true,
message: t('common.require'),
@@ -67,7 +59,7 @@ const rule = reactive<FormRules>({
trigger: 'blur',
},
],
clientSecret: [
client_secret: [
{
required: true,
message: t('common.require'),
@@ -80,7 +72,7 @@ const rule = reactive<FormRules>({
trigger: 'blur',
},
],
redirectUri: [
redirect_uri: [
{
required: true,
message: t('common.require'),
@@ -94,7 +86,7 @@ const rule = reactive<FormRules>({
},
{ required: true, validator: validateUrl, trigger: 'blur' },
],
discovery: [
metadata_url: [
{
required: true,
message: t('common.require'),
@@ -134,28 +126,23 @@ const rule = reactive<FormRules>({
trigger: 'blur',
},
],
usePkce: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
mapping: [{ required: false, validator: validateMapping, trigger: 'blur' }],
})
const edit = () => {
showLoading()
request
.get('/setting/authentication/info/oidc')
.get('/system/authentication/2')
.then((res) => {
const resData = res as Partial<OidcForm>
;(Object.keys(resData) as (keyof OidcForm)[]).forEach((key) => {
const value = resData[key]
if (value !== undefined) {
state.form[key] = value as any
}
})
if (!res?.config) {
return
}
id.value = res.id
const data = JSON.parse(res.config)
for (const key in data) {
state.form[key] = data[key] as any
}
})
.finally(() => {
closeLoading()
@@ -169,7 +156,15 @@ const submitForm = async (formEl: FormInstance | undefined) => {
await formEl.validate((valid) => {
if (valid) {
const param = { ...state.form }
const method = request.post('/setting/authentication/save/oidc', param)
const data = {
id: 2,
type: 2,
config: JSON.stringify(param),
name: 'oidc',
}
const method = id.value
? request.put('/system/authentication', data)
: request.post('/system/authentication', data)
showLoading()
method
.then((res) => {
@@ -190,6 +185,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
id.value = null
dialogVisible.value = false
}
@@ -207,16 +203,21 @@ const closeLoading = () => {
}
const validate = () => {
const url = '/setting/authentication/validate/oidc'
const data = state.form
const url = '/system/authentication/status'
const config_data = state.form
const data = {
type: 2,
name: 'oidc',
config: JSON.stringify(config_data),
}
showLoading()
request
.post(url, data)
.patch(url, data)
.then((res) => {
if (res === 'true') {
ElMessage.success(t('commons.test_connect') + t('report.last_status_success'))
if (res) {
ElMessage.success(t('ds.connection_success'))
} else {
ElMessage.error(t('commons.test_connect') + t('report.last_status_fail'))
ElMessage.error(t('ds.connection_failed'))
}
})
.finally(() => {
@@ -233,7 +234,7 @@ defineExpose({
<template>
<el-drawer
v-model="dialogVisible"
:title="t('system.oidc_settings')"
:title="t('authentication.oidc_settings')"
modal-class="platform-info-drawer"
size="600px"
direction="rtl"
@@ -246,45 +247,45 @@ defineExpose({
label-width="80px"
label-position="top"
>
<el-form-item label="Client ID" prop="clientId">
<el-input v-model="state.form.clientId" :placeholder="t('common.please_input')" />
<el-form-item :label="t('authentication.client_id')" prop="client_id">
<el-input v-model="state.form.client_id" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="Client Secret" prop="clientSecret">
<el-form-item :label="t('authentication.client_secret')" prop="client_secret">
<el-input
v-model="state.form.clientSecret"
v-model="state.form.client_secret"
type="password"
show-password
:placeholder="t('common.please_input')"
/>
</el-form-item>
<el-form-item label="Discovery" prop="discovery">
<el-input v-model="state.form.discovery" :placeholder="t('common.please_input')" />
<el-form-item :label="t('authentication.metadata_url')" prop="metadata_url">
<el-input v-model="state.form.metadata_url" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="Realm" prop="realm">
<el-form-item :label="t('authentication.realm')" prop="realm">
<el-input v-model="state.form.realm" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="Scope" prop="scope">
<el-form-item :label="t('authentication.scope')" prop="scope">
<el-input v-model="state.form.scope" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="Use Pkce" prop="usePkce">
<el-switch v-model="state.form.usePkce" />
<el-form-item :label="t('authentication.redirect_url')" prop="redirect_uri">
<el-input v-model="state.form.redirect_uri" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="Redirect Uri" prop="redirectUri">
<el-input v-model="state.form.redirectUri" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item :label="t('system.field_mapping')" prop="mapping">
<el-input v-model="state.form.mapping" :placeholder="t('system.oauth2name')" />
<el-form-item :label="t('authentication.field_mapping')" prop="mapping">
<el-input
v-model="state.form.mapping"
:placeholder="t('authentication.oidc_field_mapping_placeholder')"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button secondary @click="resetForm(oidcForm)">{{ t('common.cancel') }}</el-button>
<el-button secondary :disabled="!state.form.clientId" @click="validate">
{{ t('commons.test_connect') }}
<el-button secondary :disabled="!state.form.client_id" @click="validate">
{{ t('ds.test_connection') }}
</el-button>
<el-button type="primary" @click="submitForm(oidcForm)">
{{ t('commons.save') }}
{{ t('common.save') }}
</el-button>
</span>
</template>
@@ -91,7 +91,7 @@ const init = (needLoading: boolean) => {
.then((res) => {
if (res) {
infos.value = [...(res as CardInfo[])].filter(
(item) => item.name === 'cas' || item.name === 'oauth2'
(item) => item.name === 'cas' || item.name === 'oauth2' || item.name === 'oidc'
)
showInfos.value = [...infos.value]
}