mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-07 02:57:51 +00:00
fix(auth): hide password routing implementation details
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
0e9d2e2d3e
commit
8249e84454
6 changed files with 20 additions and 17 deletions
|
|
@ -8,9 +8,17 @@ test.describe('Auth Entry', () => {
|
|||
})
|
||||
|
||||
test('validates required fields and preserves returnTo on register link', async ({ page }) => {
|
||||
await page.route('**/runtime-config.js', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/javascript',
|
||||
body: 'window.__SKILLHUB_RUNTIME_CONFIG__ = { authDirectEnabled: "true", authDirectProvider: "local" }',
|
||||
})
|
||||
})
|
||||
await page.goto('/login?returnTo=%2Fdashboard%2Ftokens')
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Login to SkillHub' })).toBeVisible()
|
||||
await expect(page.getByText(/password compatibility layer/i)).toHaveCount(0)
|
||||
|
||||
await page.getByRole('button', { name: 'Login' }).click()
|
||||
await expect(page.getByText('Username is required')).toBeVisible()
|
||||
|
|
|
|||
|
|
@ -360,7 +360,6 @@
|
|||
"noAccount": "Don't have an account?",
|
||||
"register": "Sign up now",
|
||||
"oauthHint": "After OAuth authentication, you will be automatically redirected back to this site.",
|
||||
"passwordCompatHint": "This deployment has the password compatibility layer enabled. The form will route to {{name}} instead of the fixed local account endpoint.",
|
||||
"enterpriseSsoTitle": "Enterprise SSO",
|
||||
"enterpriseSsoHint": "This deployment has the compatibility layer enabled. If your browser already has a {{name}} session, you can try establishing a SkillHub session directly.",
|
||||
"enterpriseSsoAutoHint": "This deployment has automatic {{name}} probing enabled. If it does not succeed, you can continue with the standard login methods.",
|
||||
|
|
|
|||
|
|
@ -360,7 +360,6 @@
|
|||
"noAccount": "Нет аккаунта?",
|
||||
"register": "Зарегистрироваться",
|
||||
"oauthHint": "После аутентификации OAuth вы будете автоматически перенаправлены обратно на этот сайт.",
|
||||
"passwordCompatHint": "В этом развёртывании включён слой совместимости паролей. Форма направит запрос в {{name}} вместо фиксированной локальной учётной записи.",
|
||||
"enterpriseSsoTitle": "Корпоративный SSO",
|
||||
"enterpriseSsoHint": "В этом развёртывании включён слой совместимости. Если в браузере уже есть сессия {{name}}, можно попробовать сразу установить сессию SkillHub.",
|
||||
"enterpriseSsoAutoHint": "В этом развёртывании включено автоматическое зондирование {{name}}. Если оно не сработает, продолжайте стандартными способами входа.",
|
||||
|
|
|
|||
|
|
@ -360,7 +360,6 @@
|
|||
"noAccount": "还没有账号?",
|
||||
"register": "立即注册",
|
||||
"oauthHint": "使用 OAuth 登录时,认证完成后会自动返回当前站点。",
|
||||
"passwordCompatHint": "当前部署已启用账号密码兼容接入层。表单将路由到 {{name}},而不是固定使用本地账号接口。",
|
||||
"enterpriseSsoTitle": "企业单点登录",
|
||||
"enterpriseSsoHint": "当前部署已启用兼容接入层。若浏览器中已存在 {{name}} 会话,可直接尝试建立 SkillHub 登录态。",
|
||||
"enterpriseSsoAutoHint": "当前部署已启用自动 {{name}} 探测。若未成功,你仍可继续使用现有登录方式。",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { describe, expect, it, vi } from 'vitest'
|
|||
const authMethodsFixture = vi.hoisted(() => ({
|
||||
methods: [] as Array<{ id: string, methodType: string }>,
|
||||
bootstrapEnabled: false,
|
||||
directEnabled: false,
|
||||
isError: false,
|
||||
returnTo: '',
|
||||
navigate: vi.fn(),
|
||||
|
|
@ -38,7 +39,7 @@ vi.mock('lucide-react', () => ({
|
|||
}))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
getDirectAuthRuntimeConfig: () => ({ enabled: false }),
|
||||
getDirectAuthRuntimeConfig: () => ({ enabled: authMethodsFixture.directEnabled, provider: 'local' }),
|
||||
getSessionBootstrapRuntimeConfig: () => ({ enabled: authMethodsFixture.bootstrapEnabled, provider: 'proxy' }),
|
||||
}))
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ describe('LoginPage', () => {
|
|||
cleanup()
|
||||
authMethodsFixture.methods = []
|
||||
authMethodsFixture.bootstrapEnabled = false
|
||||
authMethodsFixture.directEnabled = false
|
||||
authMethodsFixture.isError = false
|
||||
authMethodsFixture.returnTo = ''
|
||||
authMethodsFixture.navigate.mockClear()
|
||||
|
|
@ -103,6 +105,14 @@ describe('LoginPage', () => {
|
|||
expect(html).toContain('login.register')
|
||||
})
|
||||
|
||||
it('does not expose password routing details when direct login is configured', () => {
|
||||
authMethodsFixture.directEnabled = true
|
||||
const html = renderToStaticMarkup(<LoginPage />)
|
||||
|
||||
expect(html).toContain('login.submit')
|
||||
expect(html).not.toContain('login.passwordCompatHint')
|
||||
})
|
||||
|
||||
it('returns to the home page after direct login without an explicit destination', async () => {
|
||||
render(<LoginPage />)
|
||||
fireEvent.change(screen.getByLabelText('login.username'), { target: { value: 'user1' } })
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Link, useNavigate, useSearch } from '@tanstack/react-router'
|
|||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ArrowRight, Eye, EyeOff, LockKeyhole, UserRound } from 'lucide-react'
|
||||
import { getDirectAuthRuntimeConfig, getSessionBootstrapRuntimeConfig } from '@/api/client'
|
||||
import { getSessionBootstrapRuntimeConfig } from '@/api/client'
|
||||
import { AuthShell } from '@/features/auth/auth-shell'
|
||||
import { AuthMethodButtonList } from '@/features/auth/login-button'
|
||||
import { SessionBootstrapEntry } from '@/features/auth/session-bootstrap-entry'
|
||||
|
|
@ -23,7 +23,6 @@ export function LoginPage() {
|
|||
const navigate = useNavigate()
|
||||
const search = useSearch({ from: '/login' })
|
||||
const loginMutation = usePasswordLogin()
|
||||
const directAuthConfig = getDirectAuthRuntimeConfig()
|
||||
const bootstrapConfig = getSessionBootstrapRuntimeConfig()
|
||||
const [username, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
|
@ -34,10 +33,6 @@ export function LoginPage() {
|
|||
const returnTo = resolveAuthReturnTo(search.returnTo)
|
||||
const { data: authMethods, isLoading: authMethodsLoading } = useAuthMethods(returnTo)
|
||||
const disabledMessage = search.reason === 'accountDisabled' ? t('apiError.auth.accountDisabled') : null
|
||||
const directMethod = directAuthConfig.provider
|
||||
? authMethods?.find((method) =>
|
||||
method.methodType === 'DIRECT_PASSWORD' && method.provider === directAuthConfig.provider)
|
||||
: undefined
|
||||
const bootstrapMethod = authMethods?.find((method) => method.methodType === 'SESSION_BOOTSTRAP')
|
||||
const hasOrganizationMethod = bootstrapConfig.enabled
|
||||
const hasExternalMethods = authMethods?.some((method) => method.methodType === 'OAUTH_REDIRECT')
|
||||
|
|
@ -97,13 +92,6 @@ export function LoginPage() {
|
|||
|
||||
<div hidden={loginMode !== 'personal'}>
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
{directAuthConfig.enabled ? (
|
||||
<p className="rounded-xl border border-blue-500/20 bg-blue-500/10 px-3 py-2 text-sm text-blue-700 dark:text-blue-300">
|
||||
{t('login.passwordCompatHint', {
|
||||
name: directMethod?.displayName ?? directAuthConfig.provider,
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium" htmlFor="username">{t('login.username')}</label>
|
||||
<div className="relative">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue