Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sso): init tong tech auth #3180

Open
wants to merge 1 commit into
base: c/tongtech
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/api/sso.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const getSSOList = (): Promise<DashboardSsoBackendStatus[]> => {
}

export const postSSOLogin = (
backend: 'ldap' | 'saml',
emqxDashboardSsoLdapLogin: PostSsoLoginBackendBody,
backend: 'ldap' | 'saml' | 'tongauth',
emqxDashboardSsoLdapLogin?: PostSsoLoginBackendBody,
): Promise<PostSsoLoginBackend200> => {
return http.post(`/sso/login/${backend}`, emqxDashboardSsoLdapLogin)
}
Expand Down
Binary file added src/assets/img/tongauth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/hooks/SSO/useSSO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import {
PostSsoLoginBackend200,
} from '@/types/schemas/dashboardSingleSignOn.schemas'
import { ComputedRef, Ref, computed, reactive, ref } from 'vue'
import useI18nTl from '../useI18nTl'

export const useSSOBackendsLabel = (): { getBackendLabel: (backend: string) => string } => {
const { tl } = useI18nTl('General')
const backendsLabelMap: Map<string, string> = new Map([
[DashboardSsoBackendStatusBackend.ldap, 'LDAP'],
[DashboardSsoBackendStatusBackend.saml, 'SAML 2.0'],
[DashboardSsoBackendStatusBackend.oidc, 'OIDC'],
[DashboardSsoBackendStatusBackend.tongauth, tl('tongtech')],
])
const getBackendLabel = (backend: string): string => backendsLabelMap.get(backend) || ''

Expand All @@ -36,6 +39,9 @@ export default function useSSO(): {
enabledSSOList: Ref<Array<string>>
ldapRecord: SsoLogin
hasSSOEnabled: ComputedRef<boolean>
tongTechLoginUrl: string
tongTechBackend: Ref<'tongauth'>
isTongTechAuthEnabled: ComputedRef<boolean>
ldapLogin: () => Promise<LdapLoginResult>
getEnabledSSO: () => Promise<void>
} {
Expand All @@ -49,6 +55,8 @@ export default function useSSO(): {
})
const currentLoginBackend = ref<LoginBackend>('local')

const getLoginUrl = (backend: string) => `${API_BASE_URL}/sso/login/${backend}`

const samlLoginUrl = `${API_BASE_URL}/sso/login/${DashboardSsoBackendStatusBackend.saml}`
const samlBackend = ref(DashboardSamlBackend.saml)

Expand All @@ -57,6 +65,12 @@ export default function useSSO(): {

const hasSSOEnabled = computed(() => enabledSSOList.value.length > 0)

const tongTechLoginUrl = getLoginUrl(DashboardSsoBackendStatusBackend.tongauth)
const tongTechBackend = ref(DashboardSsoBackendStatusBackend.tongauth)
const isTongTechAuthEnabled = computed(() =>
enabledSSOList.value.includes(DashboardSsoBackendStatusBackend.tongauth),
)

const getEnabledSSO = async () => {
try {
enabledSSOList.value = await getSSORunning()
Expand Down Expand Up @@ -88,6 +102,9 @@ export default function useSSO(): {
enabledSSOList,
ldapRecord,
hasSSOEnabled,
tongTechLoginUrl,
tongTechBackend,
isTongTechAuthEnabled,
ldapLogin,
getEnabledSSO,
}
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/SSO/useSSODetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,20 @@ export default (): {
fallback_methods: ['RS256'],
client_jwks: 'none',
})
const createRawTongTechForm = (): any => ({
enable: true,
backend: DashboardSsoBackendStatusBackend.tongauth,
dashboard_addr: location.origin + location.pathname.slice(0, -1),
auth_server_addr: 'http://168.1.15.162:9000',
client_id: 'tongmmp_yunjian_oauth2',
client_secret: 'b2c3d4e5-f678-9c0a-1b2f-4567890abcde',
})

const formCreatorMap: Map<string, () => any> = new Map([
[DashboardSsoBackendStatusBackend.ldap, createRawLDAPForm],
[DashboardSsoBackendStatusBackend.saml, createRawSAMLForm],
[DashboardSsoBackendStatusBackend.oidc, createRawOIDCForm],
[DashboardSsoBackendStatusBackend.tongauth, createRawTongTechForm],
])
const createRawForm = (backend: string) => {
const func = formCreatorMap.get(backend)
Expand Down
12 changes: 12 additions & 0 deletions src/i18n/Base.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,4 +703,16 @@ export default {
zh: '',
en: ' ',
},
tongTechAuthGuidance: {
zh: '请在配置文件中,{add}或启用东方通单点登录,并重启服务',
en: 'Please {add} or enable TongTech Auth in the configuration file and restart the service',
},
tongTechAuthConfigComment: {
zh: '东方通单点登录配置参数,请确保参数在 dashboard > sso 层级下',
en: 'TongTech SSO configuration parameters, please ensure the parameters are under the dashboard > sso hierarchy',
},
jumping: {
zh: '跳转中...',
en: 'Jumping...',
},
}
12 changes: 12 additions & 0 deletions src/i18n/General.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ export default {
zh: '启用 {backend} SSO',
en: 'Enable {backend} SSO',
},
tongtech: {
zh: '东方通',
en: 'TongTech',
},
baseDN: {
zh: '基本 DN',
en: 'Base DN',
Expand Down Expand Up @@ -355,6 +359,14 @@ export default {
zh: '备用方法',
en: 'Fallback Methods',
},
authServerAddr: {
zh: '认证服务器地址',
en: 'Auth Server Address',
},
authServerAddrDesc: {
zh: '东方通认证服务器的基本 URL。示例:http://168.1.15.162:9000',
en: 'The Base URL of the TongAuth Server. Example: http://168.1.15.162:9000',
},
JWK: {
zh: 'JSON Web 密钥',
en: 'JSON Web Key (JWK)',
Expand Down
107 changes: 89 additions & 18 deletions src/types/schemas/dashboardSingleSignOn.schemas.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
export type GetSsoTongauthCallback404Code =
typeof GetSsoTongauthCallback404Code[keyof typeof GetSsoTongauthCallback404Code]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetSsoTongauthCallback404Code = {
BACKEND_NOT_FOUND: 'BACKEND_NOT_FOUND',
} as const

export type GetSsoTongauthCallback404 = {
code?: GetSsoTongauthCallback404Code
message?: string
}

export type GetSsoTongauthCallback401Code =
typeof GetSsoTongauthCallback401Code[keyof typeof GetSsoTongauthCallback401Code]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetSsoTongauthCallback401Code = {
BAD_USERNAME_OR_PWD: 'BAD_USERNAME_OR_PWD',
} as const

export type GetSsoTongauthCallback401 = {
code?: GetSsoTongauthCallback401Code
message?: string
}

export type GetSsoTongauthCallback400Code =
typeof GetSsoTongauthCallback400Code[keyof typeof GetSsoTongauthCallback400Code]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetSsoTongauthCallback400Code = {
BAD_REQUEST: 'BAD_REQUEST',
} as const

export type GetSsoTongauthCallback400 = {
code?: GetSsoTongauthCallback400Code
message?: string
}

export type GetSsoTongauthCallback302Code =
typeof GetSsoTongauthCallback302Code[keyof typeof GetSsoTongauthCallback302Code]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetSsoTongauthCallback302Code = {
REDIRECT: 'REDIRECT',
} as const

export type GetSsoTongauthCallback302 = {
code?: GetSsoTongauthCallback302Code
message?: string
}

export type DeleteSsoBackend404Code =
typeof DeleteSsoBackend404Code[keyof typeof DeleteSsoBackend404Code]

Expand All @@ -23,9 +75,9 @@ export type PutSsoBackend404 = {
message?: string
}

export type PutSsoBackend200 = SsoOidc | DashboardSaml | SsoLdap
export type PutSsoBackend200 = DashboardTongauth | DashboardSaml | SsoOidc | SsoLdap

export type PutSsoBackendBody = SsoOidc | DashboardSaml | SsoLdap
export type PutSsoBackendBody = DashboardTongauth | DashboardSaml | SsoOidc | SsoLdap

export type GetSsoBackend404Code = typeof GetSsoBackend404Code[keyof typeof GetSsoBackend404Code]

Expand All @@ -39,15 +91,16 @@ export type GetSsoBackend404 = {
message?: string
}

export type GetSsoBackend200 = SsoOidc | DashboardSaml | SsoLdap
export type GetSsoBackend200 = DashboardTongauth | DashboardSaml | SsoOidc | SsoLdap

export type GetSsoRunning200Item = typeof GetSsoRunning200Item[keyof typeof GetSsoRunning200Item]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetSsoRunning200Item = {
ldap: 'ldap',
saml: 'saml',
oidc: 'oidc',
saml: 'saml',
tongauth: 'tongauth',
} as const

export type PostSsoLoginBackend404Code =
Expand Down Expand Up @@ -109,7 +162,7 @@ export type PostSsoLoginBackend200 = {
license?: PostSsoLoginBackend200License
}

export type PostSsoLoginBackendBody = SsoLogin | DashboardLogin | SsoLogin
export type PostSsoLoginBackendBody = DashboardLogin | DashboardLogin | SsoLogin | SsoLogin

export type GetSsoSamlMetadata404Code =
typeof GetSsoSamlMetadata404Code[keyof typeof GetSsoSamlMetadata404Code]
Expand Down Expand Up @@ -301,6 +354,20 @@ export const SsoLdapBackend = {
ldap: 'ldap',
} as const

export interface SsoLdap {
enable?: boolean
backend: SsoLdapBackend
query_timeout?: string
server: string
pool_size?: number
username: string
password?: string
base_dn: string
filter?: string
request_timeout?: string
ssl?: LdapSsl
}

export type SsoClientFileJwksType = typeof SsoClientFileJwksType[keyof typeof SsoClientFileJwksType]

// eslint-disable-next-line @typescript-eslint/no-redeclare
Expand Down Expand Up @@ -370,18 +437,21 @@ export interface LdapSsl {
server_name_indication?: LdapSslServerNameIndication
}

export interface SsoLdap {
export type DashboardTongauthBackend =
typeof DashboardTongauthBackend[keyof typeof DashboardTongauthBackend]

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const DashboardTongauthBackend = {
tongauth: 'tongauth',
} as const

export interface DashboardTongauth {
enable?: boolean
backend: SsoLdapBackend
query_timeout?: string
server: string
pool_size?: number
username: string
password?: string
base_dn: string
filter?: string
request_timeout?: string
ssl?: LdapSsl
backend: DashboardTongauthBackend
dashboard_addr?: string
auth_server_addr?: string
client_id?: string
client_secret?: string
}

export type DashboardSamlBackend = typeof DashboardSamlBackend[keyof typeof DashboardSamlBackend]
Expand All @@ -405,7 +475,7 @@ export type DashboardLoginBackend = typeof DashboardLoginBackend[keyof typeof Da

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const DashboardLoginBackend = {
saml: 'saml',
tongauth: 'tongauth',
} as const

export interface DashboardLogin {
Expand All @@ -418,8 +488,9 @@ export type DashboardSsoBackendStatusBackend =
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const DashboardSsoBackendStatusBackend = {
ldap: 'ldap',
saml: 'saml',
oidc: 'oidc',
saml: 'saml',
tongauth: 'tongauth',
} as const

export interface DashboardSsoBackendStatus {
Expand Down
9 changes: 7 additions & 2 deletions src/views/Base/Login.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<div class="login">
<el-container>
<div class="login" v-loading="isSSOLoading">
<TongTechAuthGuidance v-if="!isTongTechAuthEnabled" />
<TongTechAuthForm v-else />
<el-container v-if="false">
<el-header>
<img src="@/assets/img/tongtech.svg" alt="logo" height="40" />
<el-dropdown popper-class="lang-dropdown">
Expand Down Expand Up @@ -290,6 +292,8 @@ import { computed, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'
import TongTechAuthGuidance from './components/TongTechAuthGuidance.vue'
import TongTechAuthForm from './components/TongTechAuthForm.vue'

const { t } = useI18n()
const store = useStore()
Expand All @@ -309,6 +313,7 @@ const {
isSSOLoading,
ldapRecord,
hasSSOEnabled,
isTongTechAuthEnabled,
ldapLogin,
getEnabledSSO,
} = useSSO()
Expand Down
53 changes: 53 additions & 0 deletions src/views/Base/components/TongTechAuthForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<div class="tong-tech-auth">
<p class="placeholder">{{ t('Base.jumping') }}</p>
<form
v-show="false"
:action="tongTechLoginUrl"
method="post"
class="form-sso"
enctype="multipart/form-data"
>
<input
v-show="false"
type="text"
name="backend"
id="backend"
required
v-model="tongTechBackend"
/>
<input
ref="SubmitButton"
class="el-button el-button--info is-link"
type="submit"
value="TongTech"
/>
</form>
</div>
</template>

<script setup lang="ts">
import { waitAMoment } from '@/common/tools'
import useSSO from '@/hooks/SSO/useSSO'
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

const { tongTechBackend, tongTechLoginUrl } = useSSO()

const SubmitButton = ref()
onMounted(async () => {
await waitAMoment(500)
SubmitButton.value.click()
})
</script>

<style lang="scss">
.tong-tech-auth {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
</style>
Loading