mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat(X-Pack): Add CAS Authentication Mechanism
This commit is contained in:
committed by
fit2cloud-chenyw
parent
dc4595b193
commit
4c8b463df5
@@ -0,0 +1,40 @@
|
||||
"""048_authentication_ddl
|
||||
|
||||
Revision ID: 073bf544b373
|
||||
Revises: c1b794a961ce
|
||||
Create Date: 2025-10-30 14:11:29.786938
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '073bf544b373'
|
||||
down_revision = 'c1b794a961ce'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('sys_authentication',
|
||||
sa.Column('id', sa.BigInteger(), nullable=False),
|
||||
sa.Column('name', sa.String(255), nullable=False),
|
||||
sa.Column('type', sa.Integer(), nullable=False),
|
||||
sa.Column('config', sa.Text(), nullable=True),
|
||||
sa.Column('enable', sa.Boolean(), nullable=False),
|
||||
sa.Column('valid', sa.Boolean(), nullable=False),
|
||||
sa.Column('create_time', sa.BigInteger(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_sys_authentication_id'), 'sys_authentication', ['id'], unique=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_sys_authentication_id'), table_name='sys_authentication')
|
||||
op.drop_table('sys_authentication')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,42 @@
|
||||
"""049_user_platform_ddl
|
||||
|
||||
Revision ID: b58a71ca6ae3
|
||||
Revises: 073bf544b373
|
||||
Create Date: 2025-11-04 12:31:56.481582
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b58a71ca6ae3'
|
||||
down_revision = '073bf544b373'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('sys_user_platform',
|
||||
sa.Column('uid', sa.BigInteger(), nullable=False),
|
||||
sa.Column('origin', sa.Integer(), server_default='0', nullable=False),
|
||||
sa.Column('platform_uid', sa.String(255), nullable=False),
|
||||
sa.Column('id', sa.BigInteger(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_sys_user_platform_id'), 'sys_user_platform', ['id'], unique=False)
|
||||
|
||||
op.add_column('sys_user', sa.Column('origin', sa.Integer(), server_default='0', nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
op.drop_column('sys_user', 'origin')
|
||||
|
||||
op.drop_index(op.f('ix_sys_user_platform_id'), table_name='sys_user_platform')
|
||||
op.drop_table('sys_user_platform')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from apps.system.schemas.logout_schema import LogoutSchema
|
||||
from apps.system.schemas.system_schema import BaseUserDTO
|
||||
from common.core.deps import SessionDep, Trans
|
||||
from common.utils.crypto import sqlbot_decrypt
|
||||
@@ -9,6 +10,7 @@ from common.core.security import create_access_token
|
||||
from datetime import timedelta
|
||||
from common.core.config import settings
|
||||
from common.core.schemas import Token
|
||||
from sqlbot_xpack.authentication.manage import logout as xpack_logout
|
||||
router = APIRouter(tags=["login"], prefix="/login")
|
||||
|
||||
@router.post("/access-token")
|
||||
@@ -30,4 +32,10 @@ async def local_login(
|
||||
user_dict = user.to_dict()
|
||||
return Token(access_token=create_access_token(
|
||||
user_dict, expires_delta=access_token_expires
|
||||
))
|
||||
))
|
||||
|
||||
@router.post("/logout")
|
||||
async def logout(session: SessionDep, request: Request, dto: LogoutSchema):
|
||||
if dto.origin != 0:
|
||||
return await xpack_logout(session, request, dto)
|
||||
return None
|
||||
@@ -55,4 +55,16 @@ class AssistantBaseModel(SQLModel):
|
||||
|
||||
class AssistantModel(SnowflakeBase, AssistantBaseModel, table=True):
|
||||
__tablename__ = "sys_assistant"
|
||||
|
||||
|
||||
|
||||
class AuthenticationBaseModel(SQLModel):
|
||||
name: str = Field(max_length=255, nullable=False)
|
||||
type: int = Field(nullable=False, default=0)
|
||||
config: Optional[str] = Field(sa_type = Text(), nullable=True)
|
||||
|
||||
|
||||
class AuthenticationModel(SnowflakeBase, AuthenticationBaseModel, table=True):
|
||||
__tablename__ = "sys_authentication"
|
||||
create_time: Optional[int] = Field(default=0, sa_type=BigInteger())
|
||||
enable: bool = Field(default=False, nullable=False)
|
||||
valid: bool = Field(default=False, nullable=False)
|
||||
@@ -15,9 +15,20 @@ class BaseUserPO(SQLModel):
|
||||
password: str = Field(default_factory=default_md5_pwd, max_length=255)
|
||||
email: str = Field(max_length=255)
|
||||
status: int = Field(default=0, nullable=False)
|
||||
origin: int = Field(nullable=False, default=0)
|
||||
create_time: int = Field(default_factory=get_timestamp, sa_type=BigInteger(), nullable=False)
|
||||
language: str = Field(max_length=255, default="zh-CN")
|
||||
|
||||
class UserModel(SnowflakeBase, BaseUserPO, table=True):
|
||||
__tablename__ = "sys_user"
|
||||
|
||||
|
||||
class UserPlatformBase(SQLModel):
|
||||
uid: int = Field(nullable=False, sa_type=BigInteger())
|
||||
origin: int = Field(nullable=False, default=0)
|
||||
platform_uid: str = Field(max_length=255, nullable=False)
|
||||
|
||||
class UserPlatformModel(SnowflakeBase, UserPlatformBase, table=True):
|
||||
__tablename__ = "sys_user_platform"
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from enum import Enum
|
||||
|
||||
@@ -16,4 +17,5 @@ class CacheName(Enum):
|
||||
ASSISTANT_INFO = "assistant:info"
|
||||
ASSISTANT_DS = "assistant:ds"
|
||||
def __str__(self):
|
||||
return self.value
|
||||
return self.value
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LogoutSchema(BaseModel):
|
||||
token: Optional[str] = None
|
||||
flag: Optional[str] = 'default'
|
||||
origin: Optional[int] = 0
|
||||
data: Optional[str] = None
|
||||
@@ -53,6 +53,7 @@ class UserCreator(BaseUser):
|
||||
name: str = Field(min_length=1, max_length=100, description="用户名")
|
||||
email: str = Field(min_length=1, max_length=100, description="用户邮箱")
|
||||
status: int = 1
|
||||
origin: Optional[int] = 0
|
||||
oid_list: Optional[list[int]] = None
|
||||
|
||||
""" @field_validator("email")
|
||||
@@ -70,7 +71,7 @@ class UserGrid(UserEditor):
|
||||
create_time: int
|
||||
language: str = "zh-CN"
|
||||
# space_name: Optional[str] = None
|
||||
origin: str = ''
|
||||
# origin: str = ''
|
||||
|
||||
|
||||
class PwdEditor(BaseModel):
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import Tuple
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
from requests.exceptions import RequestException, Timeout
|
||||
|
||||
def verify_url(url: str, timeout: int = 5) -> Tuple[bool, str]:
|
||||
try:
|
||||
parsed = urlparse(url)
|
||||
if not all([parsed.scheme, parsed.netloc]):
|
||||
return False, "无效的 URL 格式"
|
||||
|
||||
if parsed.scheme not in ['http', 'https']:
|
||||
return False, "URL 必须以 http 或 https 开头"
|
||||
|
||||
response = requests.get(
|
||||
url,
|
||||
timeout=timeout,
|
||||
verify=False # 忽略 SSL 证书验证
|
||||
)
|
||||
|
||||
if response.status_code < 400:
|
||||
return True, "URL 可达"
|
||||
else:
|
||||
return False, f"服务器返回错误状态码: {response.status_code}"
|
||||
|
||||
except Timeout:
|
||||
return False, f"连接超时 (>{timeout}秒)"
|
||||
except RequestException as e:
|
||||
return False, f"连接失败: {str(e)}"
|
||||
except Exception as e:
|
||||
return False, f"验证过程发生错误: {str(e)}"
|
||||
@@ -33,7 +33,10 @@ wlist = [
|
||||
"/system/assistant/info/*",
|
||||
"/system/assistant/app/*",
|
||||
"/system/assistant/picture/*",
|
||||
"/datasource/uploadExcel"
|
||||
"/datasource/uploadExcel",
|
||||
"/system/authentication/platform/status",
|
||||
"/system/authentication/login/*",
|
||||
"/system/authentication/sso/*",
|
||||
]
|
||||
|
||||
class WhitelistChecker:
|
||||
|
||||
@@ -14,6 +14,6 @@ export const AuthApi = {
|
||||
},
|
||||
})
|
||||
},
|
||||
logout: () => request.post('/auth/logout'),
|
||||
logout: (data: any) => request.post('/login/logout', data),
|
||||
info: () => request.get('/user/info'),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" 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"/>
|
||||
</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"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="logo_dingtalk">
|
||||
<path id="Vector" d="M19.6422 9.97712C19.5836 10.19 19.5075 10.397 19.4137 10.5962H19.4157L19.404 10.6216C18.738 12.1177 16.9978 15.055 16.9978 15.055L16.99 15.0355L16.4822 15.9651H18.9313L14.2519 22.5L15.3143 18.0549H13.3847L14.0546 15.1156C13.5117 15.2523 12.873 15.4417 12.1133 15.6976C12.1133 15.6976 11.0879 16.3284 9.15638 14.4828C9.15638 14.4828 7.8537 13.2797 8.60953 12.977C8.92983 12.85 10.1681 12.6879 11.1426 12.5473C12.457 12.3598 13.2675 12.2602 13.2675 12.2602C13.2675 12.2602 9.21107 12.3247 8.24822 12.1645C7.28537 12.0063 6.06667 10.3189 5.80497 8.83655C5.80497 8.83655 5.40264 8.02213 6.66821 8.40688C7.93573 8.79163 13.1796 9.90681 13.1796 9.90681C13.1796 9.90681 6.36158 7.71355 5.90848 7.17842C5.45537 6.64329 4.5726 4.25277 4.68783 2.78604C4.68783 2.78604 4.73861 2.41887 5.09602 2.51652C5.09602 2.51652 10.1388 4.93829 13.5859 6.26049C17.033 7.58661 20.0289 8.26236 19.6422 9.97712Z" fill="#3296FA"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="logo_lark">
|
||||
<g id="Clip path group">
|
||||
<mask id="mask0_3028_68917" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="6" y="8" width="21" height="16">
|
||||
<g id="clip0_1621:26250">
|
||||
<path id="Vector" d="M26.5209 8H6.39999V24H26.5209V8Z" fill="white"/>
|
||||
</g>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3028_68917)">
|
||||
<g id="Group">
|
||||
<path id="Vector_2" d="M16.8551 16.4029C16.8715 16.3864 16.8891 16.3711 16.9055 16.3546C16.9395 16.3229 16.9724 16.29 17.0053 16.257L17.0733 16.1902L17.2782 15.9874L17.5567 15.7122L17.7957 15.4754L18.0204 15.2529L18.255 15.0216L18.4699 14.8089L18.7692 14.5129C18.8261 14.457 18.8843 14.4022 18.9435 14.3485C19.052 14.2487 19.1638 14.1534 19.2789 14.0613C19.3841 13.9769 19.4927 13.8957 19.6023 13.8168C19.7569 13.7072 19.9169 13.6041 20.0803 13.5077C20.2404 13.4134 20.4048 13.3246 20.5736 13.2434C20.7315 13.1667 20.8937 13.0966 21.0581 13.0318C21.1502 12.9957 21.2423 12.9617 21.3366 12.9299C21.3837 12.9134 21.4309 12.8981 21.4781 12.8838C21.056 11.2219 20.2842 9.70032 19.2438 8.4012C19.0421 8.14688 18.7329 8 18.4063 8H9.77647C9.61975 8 9.55503 8.19952 9.68111 8.2916C12.6169 10.4436 15.066 13.2182 16.8353 16.4215C16.8419 16.416 16.8485 16.4094 16.8551 16.4029Z" fill="#00D6B9"/>
|
||||
<path id="Vector_3" d="M13.4326 24C17.8889 24 21.7707 21.5411 23.7966 17.9059C23.8679 17.7787 23.937 17.6494 24.0027 17.5189C23.8997 17.7162 23.7868 17.9026 23.6629 18.078C23.619 18.1405 23.5741 18.2019 23.527 18.2622C23.4689 18.3378 23.4086 18.4102 23.3472 18.4814C23.2978 18.5373 23.2485 18.5921 23.197 18.6459C23.0929 18.7544 22.9843 18.8563 22.8714 18.9528C22.8089 19.0065 22.7454 19.058 22.6795 19.1085C22.6028 19.1666 22.525 19.2225 22.4461 19.2762C22.3956 19.3102 22.3452 19.3431 22.2937 19.3738C22.2399 19.4067 22.1862 19.4384 22.1303 19.4691C22.0185 19.5316 21.9045 19.5887 21.7883 19.6413C21.6874 19.6873 21.5844 19.729 21.4802 19.7673C21.3673 19.809 21.2522 19.8451 21.136 19.878C20.9628 19.9263 20.7874 19.9635 20.6076 19.9921C20.4782 20.0118 20.3478 20.0271 20.2151 20.0359C20.077 20.0458 19.9367 20.0491 19.7953 20.0469C19.6407 20.0447 19.485 20.0348 19.3272 20.0173C19.2121 20.0052 19.0958 19.9877 18.9807 19.9679C18.8799 19.9504 18.779 19.9285 18.6771 19.9043C18.6234 19.8912 18.5707 19.878 18.517 19.8638C18.3679 19.8232 18.2199 19.7827 18.073 19.7399C17.9996 19.7191 17.9262 19.6971 17.8527 19.6752C17.743 19.6423 17.6334 19.6095 17.5249 19.5755C17.4361 19.5481 17.3462 19.5195 17.2574 19.4899C17.173 19.4626 17.0886 19.4351 17.0042 19.4067C16.9472 19.3869 16.889 19.3683 16.8321 19.3486C16.7619 19.3244 16.6929 19.3003 16.6238 19.2762C16.5745 19.2587 16.524 19.2411 16.4747 19.2236C16.376 19.1885 16.2785 19.1523 16.1809 19.1162C16.1238 19.0953 16.068 19.0734 16.011 19.0515C15.9354 19.023 15.8597 18.9934 15.784 18.9638C15.704 18.9319 15.625 18.9013 15.545 18.8684C15.4935 18.8475 15.442 18.8267 15.3905 18.8048C15.3269 18.7785 15.2622 18.7522 15.1986 18.7248C15.1493 18.7039 15.1 18.6831 15.0506 18.6623C14.9991 18.6403 14.9476 18.6184 14.8971 18.5955C14.8533 18.5757 14.8083 18.5571 14.7645 18.5373C14.7239 18.5198 14.6834 18.5011 14.6428 18.4836C14.6022 18.465 14.5606 18.4463 14.5201 18.4277C14.4773 18.4079 14.4356 18.3893 14.3929 18.3696C14.3391 18.3443 14.2866 18.3203 14.2328 18.2951C14.1769 18.2687 14.121 18.2424 14.0662 18.2161C14.007 18.1876 13.9478 18.1591 13.8886 18.1295C13.8382 18.1043 13.7878 18.0802 13.7373 18.055C11.069 16.7208 8.70103 14.9427 6.67954 12.7885C6.57868 12.6811 6.39999 12.7523 6.39999 12.8992L6.40438 20.515V21.1333C6.40438 21.4918 6.58198 21.8294 6.88016 22.0279C8.75695 23.2732 11.0098 24 13.4326 24Z" fill="#3370FF"/>
|
||||
<path id="Vector_4" d="M23.7966 17.9059C23.7945 17.9091 23.7922 17.9124 23.7912 17.9158L23.7966 17.9059L23.8372 17.8313C23.823 17.8566 23.8098 17.8807 23.7966 17.9059Z" fill="#133C92"/>
|
||||
<path id="Vector_5" d="M23.9819 17.5573L23.9929 17.5376L23.9983 17.5277C23.9929 17.5375 23.9873 17.5474 23.9819 17.5573Z" fill="#133C92"/>
|
||||
<path id="Vector_6" d="M26.5209 13.2654C25.6175 12.8203 24.6013 12.5703 23.5258 12.5703C22.8867 12.5703 22.2685 12.658 21.683 12.8236C21.6151 12.8422 21.5482 12.863 21.4814 12.8838C21.4342 12.8992 21.387 12.9146 21.3399 12.9299C21.2457 12.9617 21.1535 12.9957 21.0614 13.0318C20.897 13.0966 20.7348 13.1667 20.577 13.2434C20.4081 13.3246 20.2437 13.4134 20.0836 13.5077C19.9192 13.6041 19.7602 13.7072 19.6056 13.8168C19.4949 13.8946 19.3874 13.9769 19.2822 14.0613C19.1671 14.1534 19.0553 14.2498 18.9468 14.3485C18.8876 14.4022 18.8294 14.457 18.7725 14.5129L18.4732 14.8089L18.2583 15.0216L18.0237 15.2529L17.799 15.4754L17.56 15.7122L17.2815 15.9874L17.0766 16.1902L17.0086 16.257C16.9757 16.29 16.9417 16.3218 16.9088 16.3546C16.8924 16.3711 16.8748 16.3864 16.8584 16.4029C16.8332 16.427 16.8079 16.4511 16.7827 16.4752C16.7542 16.5015 16.7268 16.5278 16.6983 16.5542C15.9594 17.2327 15.1339 17.8193 14.2405 18.2939C14.2931 18.3191 14.3469 18.3444 14.4006 18.3685C14.4422 18.3882 14.485 18.4079 14.5278 18.4266C14.5683 18.4452 14.6099 18.4638 14.6505 18.4825C14.691 18.5011 14.7316 18.5186 14.7722 18.5362C14.8161 18.5559 14.861 18.5757 14.9049 18.5943C14.9564 18.6162 15.0068 18.6393 15.0583 18.6612C15.1077 18.682 15.157 18.7029 15.2063 18.7237C15.2699 18.751 15.3346 18.7774 15.3982 18.8037C15.4497 18.8246 15.5012 18.8454 15.5527 18.8673C15.6317 18.899 15.7117 18.9309 15.7917 18.9626C15.8674 18.9922 15.943 19.0218 16.0186 19.0504C16.0757 19.0723 16.1316 19.0931 16.1886 19.115C16.2862 19.1512 16.3837 19.1874 16.4824 19.2225C16.5317 19.24 16.581 19.2575 16.6314 19.2751C16.7006 19.2992 16.7707 19.3234 16.8398 19.3474C16.8967 19.3672 16.9549 19.3869 17.0118 19.4055C17.0962 19.4341 17.1807 19.4614 17.2651 19.4889C17.3539 19.5174 17.4427 19.5458 17.5326 19.5744C17.6411 19.6083 17.7507 19.6413 17.8604 19.6742C17.9338 19.6961 18.0073 19.718 18.0807 19.7388C18.2276 19.7816 18.3756 19.8232 18.5247 19.8627C18.5784 19.877 18.631 19.8912 18.6848 19.9033C18.7867 19.9274 18.8876 19.9482 18.9884 19.9668C19.1046 19.9877 19.2198 20.0041 19.3349 20.0162C19.4916 20.0337 19.6484 20.0436 19.803 20.0458C19.9444 20.048 20.0836 20.0446 20.2228 20.0348C20.3554 20.025 20.4859 20.0107 20.6153 19.991C20.795 19.9635 20.9705 19.9252 21.1437 19.877C21.2599 19.8451 21.375 19.8079 21.4879 19.7662C21.5921 19.7278 21.6951 19.6862 21.796 19.6402C21.9122 19.5875 22.0262 19.5306 22.138 19.468C22.1928 19.4374 22.2476 19.4055 22.3014 19.3726C22.3529 19.3409 22.4033 19.308 22.4538 19.2751C22.5326 19.2214 22.6105 19.1654 22.6872 19.1074C22.7519 19.057 22.8166 19.0054 22.879 18.9517C22.992 18.8552 23.1006 18.7522 23.2046 18.6447C23.2562 18.591 23.3055 18.5373 23.3549 18.4803C23.4162 18.409 23.4766 18.3367 23.5346 18.261C23.5807 18.2007 23.6267 18.1394 23.6706 18.0769C23.7934 17.9026 23.9052 17.7173 24.0071 17.5222L24.1222 17.2919L25.1659 15.2123L25.1637 15.2167C25.4915 14.4844 25.9552 13.8245 26.5209 13.2654Z" fill="#133C9A"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" 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"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3028_68898)">
|
||||
<g id="CAS">
|
||||
<path d="M3.03105 12.86H4.11105V19.07H8.04106V20H3.03105V12.86ZM8.93926 12.86H11.5393C12.6893 12.86 13.5593 13.18 14.1593 13.83C14.7193 14.43 14.9993 15.3 14.9993 16.43C14.9993 17.55 14.7093 18.42 14.1393 19.04C13.5393 19.68 12.6693 20 11.5193 20H8.93926V12.86ZM10.0293 13.79V19.07H11.3193C12.2193 19.07 12.8893 18.85 13.3093 18.42C13.7193 17.99 13.9293 17.33 13.9293 16.43C13.9293 15.51 13.7193 14.85 13.3193 14.43C12.8993 14 12.2393 13.79 11.3393 13.79H10.0293ZM18.1572 12.86H19.3972L22.1472 20H20.9772L20.3072 18.16H17.2372L16.5672 20H15.4072L18.1572 12.86ZM17.5672 17.26H19.9772L18.7972 14.03H18.7572L17.5672 17.26ZM22.8455 12.86H25.8055C27.5055 12.86 28.3555 13.58 28.3555 15.03C28.3555 16.49 27.4955 17.22 25.7855 17.22H23.9355V20H22.8455V12.86ZM23.9355 13.79V16.29H25.7155C26.2555 16.29 26.6455 16.19 26.8955 15.99C27.1355 15.79 27.2655 15.47 27.2655 15.03C27.2655 14.59 27.1355 14.27 26.8855 14.09C26.6355 13.89 26.2455 13.79 25.7155 13.79H23.9355Z" fill="#3370FF"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +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>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758525435141" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9077" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M155 487.3c-34.6-8.5-40-13.3-40-28.8 0-19.8 11.5-25.2 38-25.2 19.8 0 46.9 4.4 58.4 8.5v-42.5c-12.5-4.4-33.9-8.5-60.4-8.5-54.7 0-86.3 19-86.3 69 0 42.1 14.6 56 64.8 67.9 36.1 8.8 42.8 15.2 42.8 33.3 0 21.1-10.3 28.2-41.5 28.2-24.1 0-51.5-4.4-66.5-9.6v45.2c14.2 4.1 41.1 8.5 68.6 8.5 64.8 0 90.6-24.8 90.6-70.9 0.1-44.2-12.1-61.3-68.5-75.1zM331 394.1l-87.9 235.7h53.6l20.4-58.4h95.7l20 58.4h54.6l-87.3-235.7H331z m1.3 134.5l31.2-89.4h4.1l30.6 89.4h-65.9zM680 394.2L639.2 527h-2.1l-39-132.8h-83.8v235.7h50.3V444.4h2.1l44.8 140.9h51.7l45.5-140.9h2.1v185.4h50.7V394.2zM865.8 585V394.1h-51.6v235.7h145.4V585z" p-id="9078" fill="#3370ff"></path></svg>
|
||||
|
After Width: | Height: | Size: 977 B |
@@ -82,8 +82,8 @@ const toAbout = () => {
|
||||
const savePwdHandler = () => {
|
||||
pwdFormRef.value?.submit()
|
||||
}
|
||||
const logout = () => {
|
||||
userStore.logout()
|
||||
const logout = async () => {
|
||||
await userStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -235,8 +235,8 @@ const resolveIcon = (iconName: any) => {
|
||||
const menuSelect = (e: any) => {
|
||||
router.push(e.index)
|
||||
}
|
||||
const logout = () => {
|
||||
userStore.logout()
|
||||
const logout = async () => {
|
||||
await userStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
const toSystem = () => {
|
||||
|
||||
@@ -664,6 +664,7 @@
|
||||
"system": {
|
||||
"system_settings": "System Settings",
|
||||
"appearance_settings": "Appearance Settings",
|
||||
"authentication_settings": "Authentication Settings",
|
||||
"platform_display_theme": "Platform Display Theme",
|
||||
"default_turquoise": "Default (Turquoise)",
|
||||
"tech_blue": "Tech Blue",
|
||||
@@ -692,5 +693,27 @@
|
||||
"save_and_apply": "Save and Apply",
|
||||
"setting_successfully": "Setting Successfully",
|
||||
"customize_theme_color": "Customize theme color"
|
||||
},
|
||||
"authentication": {
|
||||
"invalid": "Invalid",
|
||||
"valid": "Valid",
|
||||
"cas_settings": "CAS Settings",
|
||||
"callback_domain_name": "Callback Domain",
|
||||
"field_mapping": "User Attribute Mapping",
|
||||
"field_mapping_placeholder": "Example: {'{'}\"account\": \"saml2Account\", \"name\": \"saml2Name\", \"email\": \"email\"{'}'}",
|
||||
"incorrect_please_re_enter": "Incorrect format, please re-enter",
|
||||
"in_json_format": "Please enter JSON format"
|
||||
},
|
||||
"login": {
|
||||
"default_login": "Default",
|
||||
"ldap_login": "LDAP Login",
|
||||
"account_login": "Account Login",
|
||||
"other_login": "Other Login Methods",
|
||||
"pwd_invalid_error": "Password has expired, please contact administrator to modify or reset",
|
||||
"pwd_exp_tips": "Password will expire in {0} days, please change it as soon as possible",
|
||||
"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..."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,6 +664,7 @@
|
||||
"system": {
|
||||
"system_settings": "시스템 설정",
|
||||
"appearance_settings": "외관 설정",
|
||||
"authentication_settings": "인증 설정",
|
||||
"platform_display_theme": "플랫폼 표시 테마",
|
||||
"default_turquoise": "기본 (터키석 녹색)",
|
||||
"tech_blue": "테크 블루",
|
||||
@@ -692,5 +693,27 @@
|
||||
"save_and_apply": "저장 및 적용",
|
||||
"setting_successfully": "설정 성공",
|
||||
"customize_theme_color": "사용자 정의 테마 색상"
|
||||
},
|
||||
"authentication": {
|
||||
"invalid": "유효하지 않음",
|
||||
"valid": "유효함",
|
||||
"cas_settings": "CAS 설정",
|
||||
"callback_domain_name": "콜백 도메인",
|
||||
"field_mapping": "사용자 속성 매핑",
|
||||
"field_mapping_placeholder": "예: {'{'}\"account\": \"saml2Account\", \"name\": \"saml2Name\", \"email\": \"email\"{'}'}",
|
||||
"incorrect_please_re_enter": "형식이 잘못되었습니다. 다시 입력해 주세요",
|
||||
"in_json_format": "JSON 형식으로 입력해 주세요"
|
||||
},
|
||||
"login": {
|
||||
"default_login": "기본값",
|
||||
"ldap_login": "LDAP 로그인",
|
||||
"account_login": "계정 로그인",
|
||||
"other_login": "기타 로그인 방식",
|
||||
"pwd_invalid_error": "비밀번호가 만료되었습니다. 관리자에게 문의하여 수정 또는 재설정해 주세요",
|
||||
"pwd_exp_tips": "비밀번호가 {0}일 후에 만료됩니다.尽快尽快 비밀번호를 변경해 주세요",
|
||||
"qr_code": "QR 코드",
|
||||
"platform_disable": "{0} 설정이 활성화되지 않았습니다!",
|
||||
"input_account": "계정을 입력해 주세요",
|
||||
"redirect_2_auth": "{0} 인증으로 리디렉션 중입니다, {1}초..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,6 +664,7 @@
|
||||
"system": {
|
||||
"system_settings": "系统设置",
|
||||
"appearance_settings": "外观设置",
|
||||
"authentication_settings": "登录认证",
|
||||
"platform_display_theme": "平台显示主题",
|
||||
"default_turquoise": "默认 (松石绿)",
|
||||
"tech_blue": "科技蓝",
|
||||
@@ -692,5 +693,27 @@
|
||||
"save_and_apply": "保存并应用",
|
||||
"setting_successfully": "设置成功",
|
||||
"customize_theme_color": "自定义主题色"
|
||||
},
|
||||
"authentication": {
|
||||
"invalid": "无效",
|
||||
"valid": "有效",
|
||||
"cas_settings": "CAS 设置",
|
||||
"callback_domain_name": "回调地址",
|
||||
"field_mapping": "用户属性映射",
|
||||
"field_mapping_placeholder": "例如:{'{'}\"account\": \"saml2Account\", \"name\": \"saml2Name\", \"email\": \"email\"{'}'}",
|
||||
"incorrect_please_re_enter": "格式错误,请重新填写",
|
||||
"in_json_format": "请输入json格式"
|
||||
},
|
||||
"login": {
|
||||
"default_login": "默认",
|
||||
"ldap_login": "LDAP 登录",
|
||||
"account_login": "账号登录",
|
||||
"other_login": "其他登录方式",
|
||||
"pwd_invalid_error": "密码已过期请联系管理员修改或重置",
|
||||
"pwd_exp_tips": "密码在 {0} 天后过期,请尽快修改密码",
|
||||
"qr_code": "二维码",
|
||||
"platform_disable": "{0}设置未开启!",
|
||||
"input_account": "请输入账号",
|
||||
"redirect_2_auth": "正在跳转至 {0} 认证,{1} 秒..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import Professional from '@/views/system/professional/index.vue'
|
||||
import Training from '@/views/system/training/index.vue'
|
||||
import Prompt from '@/views/system/prompt/index.vue'
|
||||
import Appearance from '@/views/system/appearance/index.vue'
|
||||
import Authentication from '@/views/system/authentication/index.vue'
|
||||
import Permission from '@/views/system/permission/index.vue'
|
||||
import User from '@/views/system/user/User.vue'
|
||||
import Workspace from '@/views/system/workspace/index.vue'
|
||||
@@ -195,6 +196,12 @@ export const routes = [
|
||||
component: Appearance,
|
||||
meta: { title: t('system.appearance_settings') },
|
||||
},
|
||||
{
|
||||
path: 'authentication',
|
||||
name: 'authentication',
|
||||
component: Authentication,
|
||||
meta: { title: t('system.authentication_settings') },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -17,7 +17,8 @@ interface UserState {
|
||||
exp: number
|
||||
time: number
|
||||
weight: number
|
||||
[key: string]: string | number
|
||||
platformInfo: any | null
|
||||
[key: string]: string | number | any | null
|
||||
}
|
||||
|
||||
export const UserStore = defineStore('user', {
|
||||
@@ -32,6 +33,7 @@ export const UserStore = defineStore('user', {
|
||||
exp: 0,
|
||||
time: 0,
|
||||
weight: 0,
|
||||
platformInfo: null,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
@@ -68,6 +70,9 @@ export const UserStore = defineStore('user', {
|
||||
isSpaceAdmin(): boolean {
|
||||
return this.uid === '1' || !!this.weight
|
||||
},
|
||||
getPlatformInfo(): any | null {
|
||||
return this.platformInfo
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async login(formData: { username: string; password: string }) {
|
||||
@@ -75,8 +80,14 @@ export const UserStore = defineStore('user', {
|
||||
this.setToken(res.access_token)
|
||||
},
|
||||
|
||||
logout() {
|
||||
async logout() {
|
||||
const param = { ...{ token: this.token }, ...this.platformInfo }
|
||||
const res: any = await AuthApi.logout(param)
|
||||
this.clear()
|
||||
if (res) {
|
||||
window.location.href = res
|
||||
window.open(res, '_self')
|
||||
}
|
||||
},
|
||||
|
||||
async info() {
|
||||
@@ -145,6 +156,10 @@ export const UserStore = defineStore('user', {
|
||||
wsCache.set('user.weight', weight)
|
||||
this.weight = weight
|
||||
},
|
||||
setPlatformInfo(info: any | null) {
|
||||
wsCache.set('user.platformInfo', info)
|
||||
this.platformInfo = info
|
||||
},
|
||||
clear() {
|
||||
const keys: string[] = [
|
||||
'token',
|
||||
@@ -156,6 +171,7 @@ export const UserStore = defineStore('user', {
|
||||
'exp',
|
||||
'time',
|
||||
'weight',
|
||||
'platformInfo',
|
||||
]
|
||||
keys.forEach((key) => wsCache.delete('user.' + key))
|
||||
this.$reset()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export const loadScript = (url: string, jsId?: string) => {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const scriptId = jsId || 'de-fit2cloud-script-id'
|
||||
let dom = document.getElementById(scriptId)
|
||||
if (dom) {
|
||||
dom.parentElement?.removeChild(dom)
|
||||
dom = null
|
||||
}
|
||||
const script = document.createElement('script')
|
||||
|
||||
script.id = scriptId
|
||||
script.onload = function () {
|
||||
return resolve(null)
|
||||
}
|
||||
script.onerror = function () {
|
||||
return reject(new Error('Load script from '.concat(url, ' failed')))
|
||||
}
|
||||
script.src = url
|
||||
const head = document.head || document.getElementsByTagName('head')[0]
|
||||
;(document.body || head).appendChild(script)
|
||||
})
|
||||
}
|
||||
@@ -204,3 +204,37 @@ export const setCurrentColor = (color: any, element: HTMLElement = document.docu
|
||||
.toRGB()
|
||||
)
|
||||
}
|
||||
export const getQueryString = (name: string) => {
|
||||
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
|
||||
const r = window.location.search.substr(1).match(reg)
|
||||
if (r != null) {
|
||||
return unescape(r[2])
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const isLarkPlatform = () => {
|
||||
return !!getQueryString('state') && !!getQueryString('code')
|
||||
}
|
||||
|
||||
export const isPlatformClient = () => {
|
||||
return !!getQueryString('client') || getQueryString('state')?.includes('client')
|
||||
}
|
||||
|
||||
export const checkPlatform = () => {
|
||||
const flagArray = ['/casbi', 'oidcbi']
|
||||
const pathname = window.location.pathname
|
||||
if (
|
||||
!flagArray.some((flag) => pathname.includes(flag)) &&
|
||||
!isLarkPlatform() &&
|
||||
!isPlatformClient()
|
||||
) {
|
||||
return cleanPlatformFlag()
|
||||
}
|
||||
return true
|
||||
}
|
||||
export const cleanPlatformFlag = () => {
|
||||
const platformKey = 'out_auth_platform'
|
||||
wsCache.delete(platformKey)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ import { useUserStore } from '@/stores/user'
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const logout = () => {
|
||||
userStore.logout()
|
||||
const logout = async () => {
|
||||
await userStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<div class="login-content">
|
||||
<div class="login-right">
|
||||
<div class="login-logo-icon">
|
||||
<img height="52" v-if="loginBg" :src="loginBg" alt="" />
|
||||
<el-icon size="52" v-else
|
||||
<img v-if="loginBg" height="52" :src="loginBg" alt="" />
|
||||
<el-icon v-else size="52"
|
||||
><custom_small v-if="appearanceStore.themeColor !== 'default'"></custom_small>
|
||||
<LOGO_fold v-else></LOGO_fold
|
||||
></el-icon>
|
||||
@@ -52,6 +52,7 @@
|
||||
}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<Handler ref="xpackLoginHandler" jsname="L2NvbXBvbmVudC9sb2dpbi9IYW5kbGVy" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,12 +69,13 @@ import LOGO_fold from '@/assets/LOGO-fold.svg'
|
||||
import login_image from '@/assets/embedded/login_image.png'
|
||||
import { useAppearanceStoreWithOut } from '@/stores/appearance'
|
||||
import loginImage from '@/assets/blue/login-image_blue.png'
|
||||
import Handler from './xpack/Handler.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const appearanceStore = useAppearanceStoreWithOut()
|
||||
const { t } = useI18n()
|
||||
|
||||
const xpackLoginHandler = ref<any>(null)
|
||||
const loginForm = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="item CAS" @click="execute">
|
||||
<el-icon>
|
||||
<Icon name="logo_cas"><logo_cas class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<span class="name"> CAS </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import logo_cas from '@/assets/svg/logo_cas.svg'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
const emits = defineEmits(['switch-category'])
|
||||
const execute = () => {
|
||||
emits('switch-category', { category: 'cas', 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%;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
margin-top: 8px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,423 @@
|
||||
<template>
|
||||
<!-- <div v-if="loginCategory.qrcode" :class="{ 'de-qr-hidden': !qrStatus }">
|
||||
<QrTab
|
||||
v-if="qrStatus"
|
||||
:wecom="loginCategory.wecom"
|
||||
:dingtalk="loginCategory.dingtalk"
|
||||
:lark="loginCategory.lark"
|
||||
:larksuite="loginCategory.larksuite"
|
||||
/>
|
||||
</div> -->
|
||||
<el-divider v-if="anyEnable" class="de-other-login-divider">{{
|
||||
t('login.other_login')
|
||||
}}</el-divider>
|
||||
<el-form-item v-if="anyEnable" class="other-login-item">
|
||||
<div class="login-list">
|
||||
<!-- <QrcodeLdap
|
||||
v-if="loginCategory.qrcode || loginCategory.ldap"
|
||||
ref="qrcodeLdapHandler"
|
||||
:qrcode="loginCategory.qrcode"
|
||||
:ldap="loginCategory.ldap"
|
||||
@status-change="qrStatusChange"
|
||||
/>
|
||||
<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" /> -->
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <mfa-step v-if="showMfa" :mfa-data="state.mfaData" @close="showMfa = false" />
|
||||
<platform-error v-if="platformLoginMsg" :msg="platformLoginMsg" /> -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
/* import QrcodeLdap from './QrcodeLdap.vue'
|
||||
import Oidc from './Oidc.vue'
|
||||
import Oauth2 from './Oauth2.vue'
|
||||
import Saml2 from './Saml2.vue' */
|
||||
import Cas from './Cas.vue'
|
||||
// import QrTab from './QrTab.vue'
|
||||
import { request } from '@/utils/request'
|
||||
import { useCache } from '@/utils/useCache'
|
||||
|
||||
import router from '@/router'
|
||||
import { useUserStore } from '@/stores/user.ts'
|
||||
import { getQueryString, isPlatformClient } from '@/utils/utils'
|
||||
import { loadClient, type LoginCategory } from './PlatformClient'
|
||||
// import MfaStep from './MfaStep.vue'
|
||||
// import { logoutHandler } from '@/utils/logout'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
// import PlatformError from './PlatformError.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
interface Categoryparam {
|
||||
category: string
|
||||
proxy?: string
|
||||
}
|
||||
const platformLoginMsg = ref('')
|
||||
const { wsCache } = useCache()
|
||||
const userStore = useUserStore()
|
||||
const qrStatus = ref(false)
|
||||
const loginCategory = ref({} as LoginCategory)
|
||||
const anyEnable = ref(false)
|
||||
// const qrcodeLdapHandler = ref()
|
||||
const oauth2Handler = ref()
|
||||
const saml2Handler = ref()
|
||||
/* const state = reactive({
|
||||
mfaData: {
|
||||
enabled: false,
|
||||
ready: false,
|
||||
},
|
||||
}) */
|
||||
const emits = defineEmits(['switchTab', 'autoCallback'])
|
||||
const init = (cb?: () => void) => {
|
||||
queryCategoryStatus()
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
const list: any[] = res as any[]
|
||||
list.forEach((item: { name: keyof LoginCategory; enable: boolean }) => {
|
||||
loginCategory.value[item.name] = item.enable
|
||||
if (item.enable) {
|
||||
anyEnable.value = true
|
||||
}
|
||||
})
|
||||
}
|
||||
wsCache.delete('oidc-error')
|
||||
if (!loadClient(loginCategory.value)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
cb && cb()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!wsCache.get('oidc-error')) {
|
||||
wsCache.set('oidc-error', 1)
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* const qrStatusChange = (activeComponent: string) => {
|
||||
qrStatus.value = activeComponent === 'qrcode'
|
||||
if (activeComponent === 'account') {
|
||||
emits('switchTab', 'simple')
|
||||
} else if (activeComponent === 'ldap') {
|
||||
switcherCategory({ category: 'ldap', proxy: '' })
|
||||
}
|
||||
} */
|
||||
/* const showMfa = ref(false)
|
||||
const toMfa = (mfa) => {
|
||||
state.mfaData = mfa
|
||||
showMfa.value = true
|
||||
if (document.getElementsByClassName('preheat-container')?.length) {
|
||||
document.getElementsByClassName('preheat-container')[0].setAttribute('style', 'display: none;')
|
||||
}
|
||||
} */
|
||||
/* const ssoLogin = (category) => {
|
||||
const array = [
|
||||
{ category: 'ldap', proxy: '' },
|
||||
{ category: 'oidc', proxy: '/oidcbi/#' },
|
||||
{ category: 'cas', proxy: '/casbi/#' },
|
||||
{ category: 'oauth2', proxy: '/#' },
|
||||
{ category: 'saml2', proxy: '/#' },
|
||||
]
|
||||
if (category) {
|
||||
if (category === 1) {
|
||||
qrcodeLdapHandler.value?.setActive('ldap')
|
||||
}
|
||||
switcherCategory(array[category - 1])
|
||||
}
|
||||
} */
|
||||
|
||||
const switcherCategory = (param: Categoryparam) => {
|
||||
const { category, proxy } = param
|
||||
const curOrigin = window.location.origin
|
||||
const curLocation = getCurLocation()
|
||||
if (!category || category === 'simple' || category === 'ldap') {
|
||||
qrStatus.value = false
|
||||
emits('switchTab', category || 'simple')
|
||||
return
|
||||
}
|
||||
let pathname = window.location.pathname
|
||||
if (pathname) {
|
||||
pathname = pathname.substring(0, pathname.length - 1)
|
||||
}
|
||||
const nextPage = curOrigin + pathname + proxy + curLocation
|
||||
if (category === 'oauth2') {
|
||||
oauth2Handler?.value?.toLoginPage()
|
||||
return
|
||||
}
|
||||
if (category === 'saml2') {
|
||||
saml2Handler?.value?.toLoginPage()
|
||||
return
|
||||
}
|
||||
if (category === 'cas') {
|
||||
request.get('/system/authentication/login/1').then((res: any) => {
|
||||
window.location.href = res
|
||||
window.open(res, '_self')
|
||||
})
|
||||
return
|
||||
}
|
||||
window.location.href = nextPage
|
||||
}
|
||||
|
||||
const getCurLocation = () => {
|
||||
let queryRedirectPath = '/'
|
||||
if (router.currentRoute.value.query.redirect) {
|
||||
queryRedirectPath = router.currentRoute.value.query.redirect as string
|
||||
}
|
||||
return queryRedirectPath
|
||||
}
|
||||
|
||||
const casLogin = () => {
|
||||
const ticket = getQueryString('ticket')
|
||||
request
|
||||
.get('/system/authentication/sso/cas?ticket=' + ticket)
|
||||
.then((res: any) => {
|
||||
const token = res.access_token
|
||||
if (token && isPlatformClient()) {
|
||||
wsCache.set('de-platform-client', true)
|
||||
}
|
||||
userStore.setToken(token)
|
||||
userStore.setExp(res.exp)
|
||||
userStore.setTime(Date.now())
|
||||
userStore.setPlatformInfo({
|
||||
flag: 'cas',
|
||||
data: ticket,
|
||||
origin: 1,
|
||||
})
|
||||
const queryRedirectPath = getCurLocation()
|
||||
router.push({ path: queryRedirectPath })
|
||||
})
|
||||
.catch((e: any) => {
|
||||
userStore.setToken('')
|
||||
setTimeout(() => {
|
||||
// logoutHandler(true, true)
|
||||
platformLoginMsg.value = e?.message || e
|
||||
}, 1500)
|
||||
})
|
||||
}
|
||||
/* const platformLogin = (origin: number) => {
|
||||
const url = '/system/authentication/sso/cas'
|
||||
request
|
||||
.get(url)
|
||||
.then((res: any) => {
|
||||
const mfa = res?.mfa
|
||||
if (mfa?.enabled) {
|
||||
mfa['origin'] = origin
|
||||
// toMfa(mfa)
|
||||
return
|
||||
}
|
||||
const token = res.token
|
||||
if (token && isPlatformClient()) {
|
||||
wsCache.set('de-platform-client', true)
|
||||
}
|
||||
userStore.setToken(token)
|
||||
userStore.setExp(res.exp)
|
||||
userStore.setTime(Date.now())
|
||||
if (origin === 10 || isLarkPlatform()) {
|
||||
window.location.href =
|
||||
window.location.origin + window.location.pathname + window.location.hash
|
||||
} else {
|
||||
const queryRedirectPath = getCurLocation()
|
||||
router.push({ path: queryRedirectPath })
|
||||
}
|
||||
})
|
||||
.catch((e: any) => {
|
||||
userStore.setToken('')
|
||||
if (isLarkPlatform()) {
|
||||
setTimeout(() => {
|
||||
window.location.href =
|
||||
window.location.origin + window.location.pathname + window.location.hash
|
||||
}, 2000)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
// logoutHandler(true, true)
|
||||
platformLoginMsg.value = e?.message || e
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
const queryCategoryStatus = () => {
|
||||
const url = `/system/authentication/platform/status`
|
||||
return request.get(url)
|
||||
}
|
||||
|
||||
/* const wecomToken = async () => {
|
||||
const code = getQueryString('code')
|
||||
const state = getQueryString('state')
|
||||
if (!code || !state) {
|
||||
return null
|
||||
}
|
||||
const res = await request.post({ url: '/wecom/token', data: { code, state } })
|
||||
userStore.setToken(res.data)
|
||||
return res.data
|
||||
}
|
||||
|
||||
const larkToken = async () => {
|
||||
const code = getQueryString('code')
|
||||
const state = getQueryString('state')
|
||||
if (!code || !state) {
|
||||
return null
|
||||
}
|
||||
const res = await request.post({ url: '/lark/token', data: { code, state } })
|
||||
userStore.setToken(res.data)
|
||||
return res.data
|
||||
}
|
||||
|
||||
const saml2Token = (cb) => {
|
||||
const token = getQueryString('saml2Token')
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
userStore.setToken(token)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
cb && cb()
|
||||
}
|
||||
|
||||
const oauth2Token = (cb) => {
|
||||
const localCodeKey = localStorage.getItem('DE_OAUTH2_CODE_KEY') || 'code'
|
||||
const code = getQueryString(localCodeKey)
|
||||
const state = getQueryString('state')
|
||||
if (!code || !state) {
|
||||
throw Error('no code or state')
|
||||
return null
|
||||
}
|
||||
request
|
||||
.post({ url: '/oauth2/token', data: { code, state } })
|
||||
.then((res) => {
|
||||
userStore.setToken(res.data.token)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
cb && cb()
|
||||
})
|
||||
.catch(() => {
|
||||
setTimeout(() => {
|
||||
window.location.href =
|
||||
window.location.origin + window.location.pathname + window.location.hash
|
||||
}, 2000)
|
||||
})
|
||||
}
|
||||
|
||||
const larksuiteToken = async () => {
|
||||
const code = getQueryString('code')
|
||||
const state = getQueryString('state')
|
||||
if (!code || !state) {
|
||||
return null
|
||||
}
|
||||
const res = await request.post({ url: '/larksuite/token', data: { code, state } })
|
||||
userStore.setToken(res.data)
|
||||
return res.data
|
||||
}
|
||||
|
||||
const dingtalkToken = async () => {
|
||||
const code = getQueryString('code')
|
||||
const state = getQueryString('state')
|
||||
if (!code || !state) {
|
||||
return null
|
||||
}
|
||||
const res = await request.post({ url: '/dingtalk/token', data: { code, state } })
|
||||
userStore.setToken(res.data)
|
||||
return res.data
|
||||
} */
|
||||
|
||||
const callBackType = () => {
|
||||
return getQueryString('state')
|
||||
}
|
||||
|
||||
/* const auto2Platform = async () => {
|
||||
const resultParam = {
|
||||
preheat: true,
|
||||
loadingText: '加载中...',
|
||||
activeName: 'simple',
|
||||
}
|
||||
if (!checkPlatform()) {
|
||||
const res = await loginCategoryApi()
|
||||
const adminLogin = router.currentRoute?.value?.name === 'admin-login'
|
||||
if (adminLogin && (!res.data || res.data === 1)) {
|
||||
emits('autoCallback', resultParam)
|
||||
router.push('/401')
|
||||
return
|
||||
}
|
||||
if (res.data && !adminLogin) {
|
||||
if (res.data === 1) {
|
||||
resultParam.activeName = 'ldap'
|
||||
resultParam.preheat = false
|
||||
} else {
|
||||
resultParam.loadingText = '加载中...'
|
||||
document.getElementsByClassName('ed-loading-text')?.length &&
|
||||
(document.getElementsByClassName('ed-loading-text')[0]['innerText'] =
|
||||
resultParam.loadingText)
|
||||
}
|
||||
nextTick(() => {
|
||||
ssoLogin(res.data)
|
||||
})
|
||||
} else {
|
||||
resultParam.preheat = false
|
||||
}
|
||||
} else if (getQueryString('state')?.includes('fit2clouddeoauth2')) {
|
||||
resultParam.preheat = true
|
||||
}
|
||||
emits('autoCallback', resultParam)
|
||||
} */
|
||||
|
||||
onMounted(() => {
|
||||
wsCache.delete('de-platform-client')
|
||||
init(async () => {
|
||||
const state = callBackType()
|
||||
if (state?.includes('cas') && getQueryString('ticket')) {
|
||||
// platformLogin(1)
|
||||
casLogin()
|
||||
} /* else if (window.location.pathname.includes('/oidcbi/')) {
|
||||
platformLogin(2)
|
||||
} else if (state?.includes('dingtalk')) {
|
||||
await dingtalkToken()
|
||||
platformLogin(5)
|
||||
} else if (state?.includes('larksuite')) {
|
||||
await larksuiteToken()
|
||||
platformLogin(7)
|
||||
} else if (state?.includes('wecom')) {
|
||||
await wecomToken()
|
||||
platformLogin(6)
|
||||
} else if (state?.includes('lark')) {
|
||||
await larkToken()
|
||||
platformLogin(4)
|
||||
} else if (state?.includes('oauth2')) {
|
||||
oauth2Token(() => {
|
||||
platformLogin(9)
|
||||
})
|
||||
} else if (state?.includes('saml2')) {
|
||||
saml2Token(() => {
|
||||
platformLogin(10)
|
||||
})
|
||||
} else {
|
||||
auto2Platform()
|
||||
} */
|
||||
})
|
||||
})
|
||||
|
||||
/* defineExpose({
|
||||
ssoLogin,
|
||||
toMfa,
|
||||
}) */
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-list {
|
||||
margin-top: 2px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
column-gap: 16px;
|
||||
}
|
||||
.de-qr-hidden {
|
||||
display: none;
|
||||
}
|
||||
.de-other-login-divider {
|
||||
border-top: 1px solid #1f232926;
|
||||
}
|
||||
.other-login-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,184 @@
|
||||
import { loadScript } from '@/utils/RemoteJs'
|
||||
import { getQueryString } from '@/utils/utils'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
// import { useI18n } from 'vue-i18n'
|
||||
import { i18n } from '@/i18n'
|
||||
declare global {
|
||||
interface Window {
|
||||
tt: any
|
||||
h5sdk: any
|
||||
dd: any
|
||||
}
|
||||
}
|
||||
export interface LoginCategory {
|
||||
oidc?: boolean
|
||||
cas?: boolean
|
||||
ldap?: boolean
|
||||
oauth2?: boolean
|
||||
saml2?: boolean
|
||||
qrcode?: boolean
|
||||
lark?: boolean
|
||||
dingtalk?: boolean
|
||||
wecom?: boolean
|
||||
larksuite?: boolean
|
||||
}
|
||||
const t = i18n.global.t
|
||||
const flagArray = ['dingtalk', 'lark', 'larksuite']
|
||||
const urlArray = [
|
||||
'https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.25/dingtalk.open.js',
|
||||
'https://lf1-cdn-tos.bytegoofy.com/goofy/lark/op/h5-js-sdk-1.5.26.js',
|
||||
'https://lf1-cdn-tos.bytegoofy.com/goofy/lark/op/h5-js-sdk-1.5.16.js',
|
||||
]
|
||||
export const loadClient = (category: LoginCategory) => {
|
||||
const type = getQueryString('client')
|
||||
const corpid = getQueryString('corpid')
|
||||
if (type && !category[type as keyof LoginCategory]) {
|
||||
ElMessageBox.confirm(t('login.platform_disable', [t(`threshold.${type}`)]), {
|
||||
confirmButtonType: 'danger',
|
||||
type: 'warning',
|
||||
showCancelButton: false,
|
||||
confirmButtonText: t('commons.refresh'),
|
||||
cancelButtonText: t('dataset.cancel'),
|
||||
autofocus: false,
|
||||
showClose: false,
|
||||
})
|
||||
.then(() => {
|
||||
window.location.reload()
|
||||
})
|
||||
.catch(() => {})
|
||||
return false
|
||||
}
|
||||
if (
|
||||
!type ||
|
||||
!flagArray.includes(type) ||
|
||||
!category[type as keyof LoginCategory] ||
|
||||
(type === 'dingtalk' && !corpid)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
const index = flagArray.indexOf(type)
|
||||
const jsId = `fit2cloud-dataease-v2-platform-client-${type}`
|
||||
const awaitMethod = loadScript(urlArray[index], jsId)
|
||||
awaitMethod
|
||||
.then(() => {
|
||||
if (index === 0 && corpid) {
|
||||
dingtalkClientRequest(corpid)
|
||||
}
|
||||
if (index === 1) {
|
||||
larkClientRequest()
|
||||
}
|
||||
if (index === 2) {
|
||||
larksuiteClientRequest()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('加载失败')
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
const dingtalkClientRequest = (id?: string) => {
|
||||
const dd = window['dd']
|
||||
if (id && dd?.runtime?.permission?.requestAuthCode) {
|
||||
dd.runtime.permission.requestAuthCode({
|
||||
corpId: id,
|
||||
onSuccess: function (result: any) {
|
||||
const code = result.code
|
||||
const state = `fit2cloud-dingtalk-client`
|
||||
toUrl(`?code=${code}&state=${state}`)
|
||||
},
|
||||
onFail: function (err: any) {
|
||||
console.error(err)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
ElMessage.error('not success')
|
||||
}
|
||||
}
|
||||
|
||||
const larkClientRequest = async () => {
|
||||
if (!window['tt']) {
|
||||
ElMessage.error('load remote lark js error')
|
||||
return
|
||||
}
|
||||
const res = await queryAppid('lark')
|
||||
if (!res?.data?.appId) {
|
||||
ElMessage.error('get appId error')
|
||||
return
|
||||
}
|
||||
const appId = res.data.appId
|
||||
const callRequestAuthCode = () => {
|
||||
window['tt'].requestAuthCode({
|
||||
appId: appId,
|
||||
success: (res: any) => {
|
||||
const { code } = res
|
||||
const state = `fit2cloud-lark-client`
|
||||
toUrl(`?code=${code}&state=${state}`)
|
||||
},
|
||||
fail: (error: any) => {
|
||||
const { errno, errString } = error
|
||||
ElMessage.error(`error code: ${errno}, error msg: ${errString}`)
|
||||
},
|
||||
})
|
||||
}
|
||||
if (window['tt'].requestAccess) {
|
||||
window['tt'].requestAccess({
|
||||
appID: appId,
|
||||
scopeList: [],
|
||||
success: (res: any) => {
|
||||
const { code } = res
|
||||
const state = `fit2cloud-lark-client`
|
||||
toUrl(`?code=${code}&state=${state}`)
|
||||
},
|
||||
fail: (error: any) => {
|
||||
const { errno, errString } = error
|
||||
if (errno === 103) {
|
||||
callRequestAuthCode()
|
||||
} else {
|
||||
ElMessage.error(`error code: ${errno}, error msg: ${errString}`)
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
callRequestAuthCode()
|
||||
}
|
||||
}
|
||||
|
||||
const larksuiteClientRequest = async () => {
|
||||
if (!window['tt'] || !window['h5sdk']) {
|
||||
ElMessage.error('load remote lark js error')
|
||||
return
|
||||
}
|
||||
const res = await queryAppid('larksuite')
|
||||
if (!res?.data?.appId) {
|
||||
ElMessage.error('get appId error')
|
||||
return
|
||||
}
|
||||
const appId = res.data.appId
|
||||
|
||||
window['h5sdk'].ready(() => {
|
||||
window['tt'].requestAuthCode({
|
||||
appId: appId,
|
||||
success(res: any) {
|
||||
const code = res?.code || res
|
||||
const state = `fit2cloud-larksuite-client`
|
||||
toUrl(`?code=${code}&state=${state}`)
|
||||
},
|
||||
fail(error: any) {
|
||||
const { errno, errString } = error
|
||||
ElMessage.error(`error code: ${errno}, error msg: ${errString}`)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const queryAppid = (type: string) => {
|
||||
const url = `/${type}/qrinfo`
|
||||
return request.get(url)
|
||||
}
|
||||
|
||||
const toUrl = (url: string) => {
|
||||
const { origin, pathname } = window.location
|
||||
window.location.href = origin + pathname + url
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
|
||||
const casForm = ref<FormInstance>()
|
||||
const id = ref<number | null>(null)
|
||||
interface CasForm {
|
||||
idpUri?: string
|
||||
casCallbackDomain?: string
|
||||
mapping?: string
|
||||
}
|
||||
const state = reactive({
|
||||
form: reactive<CasForm>({
|
||||
idpUri: '',
|
||||
casCallbackDomain: '',
|
||||
mapping: '',
|
||||
}),
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateUrl = (rule, value, callback) => {
|
||||
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error(t('authentication.incorrect_please_re_enter')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateMapping = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback()
|
||||
}
|
||||
if (value.startsWith('{') === false || value.endsWith('}') === false) {
|
||||
callback(new Error(t('authentication.in_json_format')))
|
||||
return
|
||||
}
|
||||
try {
|
||||
JSON.parse(value)
|
||||
} catch {
|
||||
callback(new Error(t('authentication.in_json_format')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const rule = reactive<FormRules>({
|
||||
idpUri: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
casCallbackDomain: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
mapping: [{ required: false, validator: validateMapping, trigger: 'blur' }],
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
showLoading()
|
||||
request
|
||||
.get('/system/authentication/1')
|
||||
.then((res) => {
|
||||
if (!res?.config) {
|
||||
return
|
||||
}
|
||||
id.value = res.id
|
||||
const data = JSON.parse(res.config)
|
||||
const resData = data as Partial<CasForm>
|
||||
;(Object.keys(resData) as (keyof CasForm)[]).forEach((key) => {
|
||||
const value = resData[key]
|
||||
if (value !== undefined) {
|
||||
state.form[key] = value as any
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const emits = defineEmits(['saved'])
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const param = { ...state.form }
|
||||
const data = {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: JSON.stringify(param),
|
||||
name: 'cas',
|
||||
}
|
||||
const method = id.value
|
||||
? request.put('/system/authentication', data)
|
||||
: request.post('/system/authentication', data)
|
||||
showLoading()
|
||||
method
|
||||
.then((res) => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
id.value = null
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(casForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: '.platform-info-drawer',
|
||||
})
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const url = '/system/authentication/status'
|
||||
const config_data = state.form
|
||||
const data = {
|
||||
type: 1,
|
||||
name: 'cas',
|
||||
config: JSON.stringify(config_data),
|
||||
}
|
||||
showLoading()
|
||||
request
|
||||
.patch(url, data)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
ElMessage.success(t('ds.connection_success'))
|
||||
} else {
|
||||
ElMessage.error(t('ds.connection_failed'))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
emits('saved')
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
edit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:title="t('authentication.cas_settings')"
|
||||
modal-class="platform-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="casForm"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
label-width="80px"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item label="IdpUri" prop="idpUri">
|
||||
<el-input v-model="state.form.idpUri" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('authentication.callback_domain_name')" prop="casCallbackDomain">
|
||||
<el-input v-model="state.form.casCallbackDomain" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('authentication.field_mapping')" prop="mapping">
|
||||
<el-input
|
||||
v-model="state.form.mapping"
|
||||
:placeholder="t('authentication.field_mapping_placeholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button secondary @click="resetForm(casForm)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button secondary :disabled="!state.form.idpUri" @click="validate">
|
||||
{{ t('ds.test_connection') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitForm(casForm)">
|
||||
{{ t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.platform-info-drawer {
|
||||
.ed-drawer__footer {
|
||||
height: 64px !important;
|
||||
padding: 16px 24px !important;
|
||||
.dialog-footer {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
.ed-form-item__label {
|
||||
line-height: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.platform-info-drawer {
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.is-error {
|
||||
margin-bottom: 40px !important;
|
||||
}
|
||||
.input-with-select {
|
||||
.ed-input-group__prepend {
|
||||
width: 72px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
color: #1f2329;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,288 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
|
||||
const mappingTips = ref(t('system.for_example'))
|
||||
const ldapForm = ref<FormInstance>()
|
||||
interface LdapForm {
|
||||
addr?: string
|
||||
dn?: string
|
||||
pwd?: string
|
||||
ou?: string
|
||||
filter?: string
|
||||
mapping?: string
|
||||
}
|
||||
const state = reactive({
|
||||
form: reactive<LdapForm>({
|
||||
addr: '',
|
||||
dn: '',
|
||||
pwd: '',
|
||||
ou: '',
|
||||
filter: '',
|
||||
mapping: '',
|
||||
}),
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateMapping = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback()
|
||||
}
|
||||
try {
|
||||
JSON.parse(value)
|
||||
} catch (e) {
|
||||
callback(new Error(t('system.in_json_format')))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const rule = reactive<FormRules>({
|
||||
addr: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
dn: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
pwd: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
ou: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
filter: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
mapping: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{ required: true, validator: validateMapping, trigger: 'blur' },
|
||||
],
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
showLoading()
|
||||
request
|
||||
.get('/setting/authentication/info/ldap')
|
||||
.then((res) => {
|
||||
const resData = res as Partial<LdapForm>
|
||||
;(Object.keys(resData) as (keyof LdapForm)[]).forEach((key) => {
|
||||
const value = resData[key]
|
||||
if (value !== undefined) {
|
||||
state.form[key] = value as any
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const emits = defineEmits(['saved'])
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const param = { ...state.form }
|
||||
const method = request.post('/setting/authentication/save/ldap', param)
|
||||
showLoading()
|
||||
method
|
||||
.then((res) => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(ldapForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: '.platform-info-drawer',
|
||||
})
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const url = '/setting/authentication/validate/ldap'
|
||||
const data = state.form
|
||||
showLoading()
|
||||
request
|
||||
.post(url, data)
|
||||
.then((res) => {
|
||||
if (res === 'true') {
|
||||
ElMessage.success(t('commons.test_connect') + t('report.last_status_success'))
|
||||
} else {
|
||||
ElMessage.error(t('commons.test_connect') + t('report.last_status_fail') + ': ' + res.data)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
emits('saved')
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
edit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:title="t('system.ldap_settings')"
|
||||
modal-class="platform-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="ldapForm"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
label-width="80px"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item :label="t('system.ldap_address')" prop="addr">
|
||||
<el-input
|
||||
v-model="state.form.addr"
|
||||
:placeholder="t('common.please_input') + t('system.such_as_ldap')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.bind_dn')" prop="dn">
|
||||
<el-input v-model="state.form.dn" :placeholder="t('common.please_input') + 'DN'" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('common.pwd')" prop="pwd">
|
||||
<el-input
|
||||
v-model="state.form.pwd"
|
||||
type="password"
|
||||
show-password
|
||||
:placeholder="t('common.please_input') + t('common.pwd')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.user_ou')" prop="ou">
|
||||
<el-input
|
||||
v-model="state.form.ou"
|
||||
:placeholder="t('common.please_input') + t('system.separate_each_ou')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.user_filter')" prop="filter">
|
||||
<el-input
|
||||
v-model="state.form.filter"
|
||||
:placeholder="t('common.please_input') + t('system.such_as_uid')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.ldap_attribute_mapping')" prop="mapping">
|
||||
<el-input v-model="state.form.mapping" :placeholder="mappingTips" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button secondary @click="resetForm(ldapForm)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button secondary :disabled="!state.form.addr" @click="validate">
|
||||
{{ t('commons.test_connect') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitForm(ldapForm)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.platform-info-drawer {
|
||||
.ed-drawer__footer {
|
||||
height: 64px !important;
|
||||
padding: 16px 24px !important;
|
||||
.dialog-footer {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
.ed-form-item__label {
|
||||
line-height: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.platform-info-drawer {
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.is-error {
|
||||
margin-bottom: 40px !important;
|
||||
}
|
||||
.input-with-select {
|
||||
.ed-input-group__prepend {
|
||||
width: 72px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
color: #1f2329;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,354 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
|
||||
const oauth2Form = ref<FormInstance>()
|
||||
interface Oauth2Form {
|
||||
authEndpoint?: string
|
||||
tokenEndpoint?: string
|
||||
userInfoEndpoint?: string
|
||||
scope?: string
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
redirectUri?: string
|
||||
mapping?: string
|
||||
authMethod?: string
|
||||
}
|
||||
const state = reactive({
|
||||
form: reactive<Oauth2Form>({
|
||||
tokenEndpoint: '',
|
||||
userInfoEndpoint: '',
|
||||
scope: '',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
redirectUri: '',
|
||||
mapping: '',
|
||||
authMethod: '0',
|
||||
}),
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateUrl = (rule, value, callback) => {
|
||||
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error(t('system.incorrect_please_re_enter_de')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateMapping = (rule, value, callback) => {
|
||||
if (value === null || value === '') {
|
||||
callback()
|
||||
}
|
||||
try {
|
||||
JSON.parse(value)
|
||||
} catch (e) {
|
||||
callback(new Error(t('system.in_json_format')))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const rule = reactive<FormRules>({
|
||||
authEndpoint: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
tokenEndpoint: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
userInfoEndpoint: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
|
||||
scope: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 50,
|
||||
message: t('commons.input_limit', [2, 50]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
clientId: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [2, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
clientSecret: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 5,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [5, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
redirectUri: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
mapping: [{ required: false, validator: validateMapping, trigger: 'blur' }],
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
showLoading()
|
||||
request
|
||||
.get('/setting/authentication/info/oauth2')
|
||||
.then((res) => {
|
||||
const resData = res as Partial<Oauth2Form>
|
||||
;(Object.keys(resData) as (keyof Oauth2Form)[]).forEach((key) => {
|
||||
const value = resData[key]
|
||||
if (value !== undefined) {
|
||||
state.form[key] = value as any
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const emits = defineEmits(['saved'])
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const param = { ...state.form }
|
||||
const method = request.post('/setting/authentication/save/oauth2', param)
|
||||
showLoading()
|
||||
method
|
||||
.then((res) => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(oauth2Form.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: '.platform-info-drawer',
|
||||
})
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const url = '/setting/authentication/validate/oauth2'
|
||||
const data = state.form
|
||||
showLoading()
|
||||
request
|
||||
.post(url, data)
|
||||
.then((res) => {
|
||||
if (res === 'true') {
|
||||
ElMessage.success(t('commons.test_connect') + t('report.last_status_success'))
|
||||
} else {
|
||||
ElMessage.error(t('commons.test_connect') + t('report.last_status_fail'))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
emits('saved')
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
edit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:title="t('system.oauth2_settings')"
|
||||
modal-class="platform-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="oauth2Form"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
label-width="80px"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item :label="t('system.authorization_end_address')" prop="authEndpoint">
|
||||
<el-input v-model="state.form.authEndpoint" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('system.token_end_address')" prop="tokenEndpoint">
|
||||
<el-input v-model="state.form.tokenEndpoint" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('system.information_end_address')" prop="userInfoEndpoint">
|
||||
<el-input v-model="state.form.userInfoEndpoint" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.connection_range')" prop="scope">
|
||||
<el-input v-model="state.form.scope" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.client_id')" prop="clientId">
|
||||
<el-input v-model="state.form.clientId" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.client_key')" prop="clientSecret">
|
||||
<el-input
|
||||
v-model="state.form.clientSecret"
|
||||
type="password"
|
||||
show-password
|
||||
:placeholder="t('common.please_input')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('system.callback_address')" 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>
|
||||
|
||||
<el-form-item :label="t('datasource.auth_method')" prop="authMethod">
|
||||
<el-radio-group v-model="state.form.authMethod">
|
||||
<el-radio label="0">Authorization Code</el-radio>
|
||||
<el-radio label="1">Client Secret Jwt</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button secondary @click="resetForm(oauth2Form)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button secondary :disabled="!state.form.clientId" @click="validate">
|
||||
{{ t('commons.test_connect') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitForm(oauth2Form)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.platform-info-drawer {
|
||||
.ed-drawer__footer {
|
||||
height: 64px !important;
|
||||
padding: 16px 24px !important;
|
||||
.dialog-footer {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
.ed-form-item__label {
|
||||
line-height: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.platform-info-drawer {
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.is-error {
|
||||
margin-bottom: 40px !important;
|
||||
}
|
||||
.input-with-select {
|
||||
.ed-input-group__prepend {
|
||||
width: 72px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
color: #1f2329;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,333 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
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 state = reactive({
|
||||
form: reactive<OidcForm>({
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
discovery: '',
|
||||
redirectUri: '',
|
||||
realm: '',
|
||||
scope: '',
|
||||
usePkce: false,
|
||||
mapping: '',
|
||||
}),
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateUrl = (rule, value, callback) => {
|
||||
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error(t('system.incorrect_please_re_enter')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const validateMapping = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback()
|
||||
}
|
||||
try {
|
||||
JSON.parse(value)
|
||||
} catch (e) {
|
||||
callback(new Error(t('system.in_json_format')))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const rule = reactive<FormRules>({
|
||||
clientId: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 50,
|
||||
message: t('commons.input_limit', [2, 50]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
clientSecret: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 5,
|
||||
max: 50,
|
||||
message: t('commons.input_limit', [5, 50]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
redirectUri: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
discovery: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
realm: [
|
||||
{
|
||||
required: false,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 50,
|
||||
message: t('commons.input_limit', [2, 50]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
scope: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [2, 255]),
|
||||
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')
|
||||
.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
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const emits = defineEmits(['saved'])
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const param = { ...state.form }
|
||||
const method = request.post('/setting/authentication/save/oidc', param)
|
||||
showLoading()
|
||||
method
|
||||
.then((res) => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(oidcForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: '.platform-info-drawer',
|
||||
})
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const url = '/setting/authentication/validate/oidc'
|
||||
const data = state.form
|
||||
showLoading()
|
||||
request
|
||||
.post(url, data)
|
||||
.then((res) => {
|
||||
if (res === 'true') {
|
||||
ElMessage.success(t('commons.test_connect') + t('report.last_status_success'))
|
||||
} else {
|
||||
ElMessage.error(t('commons.test_connect') + t('report.last_status_fail'))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
emits('saved')
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
edit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:title="t('system.oidc_settings')"
|
||||
modal-class="platform-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="oidcForm"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
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>
|
||||
|
||||
<el-form-item label="Client Secret" prop="clientSecret">
|
||||
<el-input
|
||||
v-model="state.form.clientSecret"
|
||||
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>
|
||||
<el-form-item label="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-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>
|
||||
<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>
|
||||
</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>
|
||||
<el-button type="primary" @click="submitForm(oidcForm)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.platform-info-drawer {
|
||||
.ed-drawer__footer {
|
||||
height: 64px !important;
|
||||
padding: 16px 24px !important;
|
||||
.dialog-footer {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
.ed-form-item__label {
|
||||
line-height: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.platform-info-drawer {
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.is-error {
|
||||
margin-bottom: 40px !important;
|
||||
}
|
||||
.input-with-select {
|
||||
.ed-input-group__prepend {
|
||||
width: 72px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
color: #1f2329;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,259 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import { request } from '@/utils/request'
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
|
||||
const samlForm = ref<FormInstance>()
|
||||
interface SamlForm {
|
||||
idpMetaUrl?: string
|
||||
privateKey?: string
|
||||
certificate?: string
|
||||
wantAuthnRequestsSigned: boolean
|
||||
wantAssertionsSigned: boolean
|
||||
mapping?: string
|
||||
}
|
||||
const state = reactive({
|
||||
form: reactive<SamlForm>({
|
||||
idpMetaUrl: '',
|
||||
privateKey: '',
|
||||
certificate: '',
|
||||
wantAuthnRequestsSigned: false,
|
||||
wantAssertionsSigned: false,
|
||||
mapping: '',
|
||||
}),
|
||||
})
|
||||
|
||||
/* const spMetaDataUrl = ref('')
|
||||
spMetaDataUrl.value = location.origin + location.pathname + 'saml/metadata' */
|
||||
|
||||
const rule = reactive<FormRules>({
|
||||
/* idpUri: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 10,
|
||||
max: 255,
|
||||
message: t('commons.input_limit', [10, 255]),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ required: true, validator: validateUrl, trigger: 'blur' },
|
||||
], */
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
showLoading()
|
||||
request
|
||||
.get('/setting/authentication/info/saml')
|
||||
.then((res) => {
|
||||
const resData = res as Partial<SamlForm>
|
||||
;(Object.keys(resData) as (keyof SamlForm)[]).forEach((key) => {
|
||||
const value = resData[key]
|
||||
if (value !== undefined) {
|
||||
;(state.form as any)[key] = value as any
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const emits = defineEmits(['saved'])
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const param = { ...state.form } as any
|
||||
param['spEntityId'] = location.origin + location.pathname + 'saml/metadata'
|
||||
param['spAcs'] = location.origin + location.pathname + 'saml/sso'
|
||||
const method = request.post('/setting/authentication/save/saml', param)
|
||||
showLoading()
|
||||
method
|
||||
.then((res) => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(samlForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: '.platform-info-drawer',
|
||||
})
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const url = '/setting/authentication/validate/saml2'
|
||||
const data = { ...state.form } as any
|
||||
data['spEntityId'] = location.origin + location.pathname + 'saml/metadata'
|
||||
data['spAcs'] = location.origin + location.pathname + 'saml/sso'
|
||||
showLoading()
|
||||
request
|
||||
.post(url, data)
|
||||
.then((res) => {
|
||||
if (res?.data === 'true') {
|
||||
ElMessage.success(t('commons.test_connect') + t('report.last_status_success'))
|
||||
} else {
|
||||
ElMessage.error(t('commons.test_connect') + t('report.last_status_fail'))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
emits('saved')
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
edit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:title="t('system.saml.title')"
|
||||
modal-class="platform-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="samlForm"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
label-width="80px"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item label="Idp MetaData Url" prop="idpMetaUrl">
|
||||
<el-input v-model="state.form.idpMetaUrl" :placeholder="t('common.please_input')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开启请求签名" prop="wantAuthnRequestsSigned">
|
||||
<el-switch v-model="state.form.wantAuthnRequestsSigned" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开启断言签名" prop="wantAssertionsSigned">
|
||||
<el-switch v-model="state.form.wantAssertionsSigned" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="state.form.wantAuthnRequestsSigned || state.form.wantAssertionsSigned"
|
||||
label="SP PrivateKey"
|
||||
prop="privateKey"
|
||||
>
|
||||
<el-input
|
||||
v-model="state.form.privateKey"
|
||||
type="password"
|
||||
show-password
|
||||
:placeholder="t('common.please_input')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="state.form.wantAuthnRequestsSigned || state.form.wantAssertionsSigned"
|
||||
label="SP Certificate"
|
||||
prop="certificate"
|
||||
>
|
||||
<el-input
|
||||
v-model="state.form.certificate"
|
||||
type="password"
|
||||
show-password
|
||||
: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.saml2name')" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="SP MetaData Url">
|
||||
<el-input
|
||||
v-model="spMetaDataUrl"
|
||||
readonly
|
||||
:placeholder="t('system.saml2name')"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button secondary @click="resetForm(samlForm)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button secondary :disabled="!state.form.idpMetaUrl" @click="validate">
|
||||
{{ t('commons.test_connect') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitForm(samlForm)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.platform-info-drawer {
|
||||
.ed-drawer__footer {
|
||||
height: 64px !important;
|
||||
padding: 16px 24px !important;
|
||||
.dialog-footer {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
.ed-form-item__label {
|
||||
line-height: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.platform-info-drawer {
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.is-error {
|
||||
margin-bottom: 40px !important;
|
||||
}
|
||||
.input-with-select {
|
||||
.ed-input-group__prepend {
|
||||
width: 72px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
color: #1f2329;
|
||||
text-align: center;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="authentication">
|
||||
<p class="router-title">{{ t('system.authentication_settings') }}</p>
|
||||
<div v-loading="loading" class="authentication-content">
|
||||
<div class="auth-card-container">
|
||||
<div v-for="item in showInfos" :key="item.name" class="authentication-card">
|
||||
<div class="inner-card">
|
||||
<div class="inner-card-info">
|
||||
<span class="card-info-left">
|
||||
<span class="card-span">{{
|
||||
item.name === 'oauth2' ? 'OAuth2' : item.name.toLocaleUpperCase()
|
||||
}}</span>
|
||||
<span
|
||||
class="card-status"
|
||||
:class="{
|
||||
'card-hidden-status': !item.id,
|
||||
'valid-status': item.id && item.valid,
|
||||
}"
|
||||
>{{ item.valid ? t('authentication.valid') : t('authentication.invalid') }}</span
|
||||
>
|
||||
</span>
|
||||
<el-tooltip
|
||||
v-if="!item.valid"
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('system.be_turned_on')"
|
||||
placement="top"
|
||||
>
|
||||
<el-switch
|
||||
v-model="item.enable"
|
||||
:disabled="!item.valid"
|
||||
@change="switchEnable(item)"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<el-switch v-else v-model="item.enable" @change="switchEnable(item)" />
|
||||
</div>
|
||||
<div class="inner-card-btn">
|
||||
<el-button secondary @click="editInfo(item)">{{ t('datasource.edit') }}</el-button>
|
||||
<el-button class="card-validate-btn" secondary @click="validate(item.id)">{{
|
||||
t('ds.test_connection')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<cas-editor ref="casEdit" @saved="init" />
|
||||
<oidc-editor ref="oidcEdit" @saved="init" />
|
||||
<ldap-editor ref="ldapEdit" @saved="init" />
|
||||
<oauth2-editor ref="oauth2Edit" @saved="init" />
|
||||
<saml2-editor ref="saml2Edit" @saved="init" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { request } from '@/utils/request'
|
||||
import CasEditor from './CasEditor.vue'
|
||||
import LdapEditor from './LdapEditor.vue'
|
||||
import OidcEditor from './OidcEditor.vue'
|
||||
import Oauth2Editor from './Oauth2Editor.vue'
|
||||
import Saml2Editor from './SAML2Editor.vue'
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
|
||||
const { t } = useI18n()
|
||||
const loading = ref(false)
|
||||
interface CardInfo {
|
||||
id: string
|
||||
name: string
|
||||
valid: boolean
|
||||
enable: boolean
|
||||
}
|
||||
const casEdit = ref()
|
||||
const oidcEdit = ref()
|
||||
const oauth2Edit = ref()
|
||||
const saml2Edit = ref()
|
||||
const ldapEdit = ref()
|
||||
const infos = ref([] as CardInfo[])
|
||||
|
||||
const showInfos = ref([] as CardInfo[])
|
||||
|
||||
const init = (needLoading: boolean) => {
|
||||
if (needLoading) {
|
||||
loading.value = true
|
||||
}
|
||||
|
||||
const url = '/system/authentication'
|
||||
request
|
||||
.get(url)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
infos.value = [...(res as CardInfo[])].filter((item) => item.name === 'cas')
|
||||
showInfos.value = [...infos.value]
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const switchEnable = (item: CardInfo) => {
|
||||
const url = '/system/authentication/enable'
|
||||
const data = { id: item.id, enable: item.enable }
|
||||
loading.value = true
|
||||
request
|
||||
.patch(url, data)
|
||||
.then(() => {
|
||||
init(false)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const editInfo = (item: CardInfo) => {
|
||||
if (item.name === 'oidc') {
|
||||
oidcEdit.value?.edit()
|
||||
} else if (item.name === 'cas') {
|
||||
casEdit.value?.edit()
|
||||
} else if (item.name === 'ldap') {
|
||||
ldapEdit.value?.edit()
|
||||
} else if (item.name === 'oauth2') {
|
||||
oauth2Edit.value?.edit()
|
||||
} else if (item.name === 'saml2') {
|
||||
saml2Edit.value?.edit()
|
||||
}
|
||||
}
|
||||
const validate = (id: any) => {
|
||||
if (!id) {
|
||||
ElMessage.error(
|
||||
`${t('ds.test_connection') + t('report.last_status_fail')}: ${t(
|
||||
'system.platform_information_first'
|
||||
)}`
|
||||
)
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
request
|
||||
.patch('/system/authentication/status', { type: id, name: '', config: '' })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
ElMessage.success(t('ds.connection_success'))
|
||||
} else {
|
||||
ElMessage.error(t('ds.connection_failed'))
|
||||
}
|
||||
init(false)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
init(true)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.authentication {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
.router-title {
|
||||
color: #1f2329;
|
||||
font-feature-settings:
|
||||
'clig' off,
|
||||
'liga' off;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
}
|
||||
.authentication-content {
|
||||
padding: 16px 0 24px 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 28px);
|
||||
}
|
||||
.auth-card-container {
|
||||
height: initial;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
.authentication-card {
|
||||
width: calc(25% - 12px);
|
||||
min-width: 230px;
|
||||
height: 116px;
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee0e3;
|
||||
.inner-card {
|
||||
position: relative;
|
||||
.inner-card-info {
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.card-info-left {
|
||||
width: calc(100% - 40px);
|
||||
.card-span {
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
}
|
||||
.card-hidden-status {
|
||||
display: none;
|
||||
}
|
||||
.valid-status {
|
||||
background-color: #34c72433 !important;
|
||||
color: #2ca91f !important;
|
||||
}
|
||||
.card-status {
|
||||
margin-left: 8px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 2px;
|
||||
background-color: #f54a4533;
|
||||
color: #d03f3b;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.ed-switch {
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
.inner-card-btn {
|
||||
float: left;
|
||||
height: 28px;
|
||||
margin-top: 16px;
|
||||
button {
|
||||
height: 28px;
|
||||
min-width: 46px !important;
|
||||
padding: 4px 11px !important;
|
||||
:deep(span) {
|
||||
height: 20px !important;
|
||||
line-height: 20px !important;
|
||||
font-size: 12px !important;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.card-validate-btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user