diff --git a/ui/packages/tidb-dashboard-for-debugportal/package.json b/ui/packages/tidb-dashboard-for-debugportal/package.json index 2dff605a74..6880a6b79a 100644 --- a/ui/packages/tidb-dashboard-for-debugportal/package.json +++ b/ui/packages/tidb-dashboard-for-debugportal/package.json @@ -1,6 +1,6 @@ { "name": "@pingcap/tidb-dashboard-for-debugportal", - "version": "0.0.5", + "version": "0.0.6", "main": "dist/dashboardApp.js", "module": "dist/dashboardApp.js", "files": [ diff --git a/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/landing.png b/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/landing.png deleted file mode 100644 index b8ed133f02..0000000000 Binary files a/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/landing.png and /dev/null differ diff --git a/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/logo.svg b/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/logo.svg deleted file mode 100644 index 99333a9b48..0000000000 --- a/ui/packages/tidb-dashboard-for-debugportal/public/distro-res/logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/apps/UserProfile/context.ts b/ui/packages/tidb-dashboard-for-debugportal/src/apps/UserProfile/context.ts index f0e237497e..d3dcf52818 100644 --- a/ui/packages/tidb-dashboard-for-debugportal/src/apps/UserProfile/context.ts +++ b/ui/packages/tidb-dashboard-for-debugportal/src/apps/UserProfile/context.ts @@ -11,7 +11,6 @@ import client, { CodeShareRequest, MetricsPutCustomPromAddressRequest } from '~/client' -import auth from '~/uilts/auth' class DataSource implements IUserProfileDataSource { userGetSignOutInfo(redirectUrl?: string, options?: ReqConfig) { @@ -55,9 +54,7 @@ class DataSource implements IUserProfileDataSource { } class EventHandler implements IUserProfileEvent { - logOut(): void { - auth.clearAuthToken() - } + logOut(): void {} } export const ctx: IUserProfileContext = { diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/client/index.tsx b/ui/packages/tidb-dashboard-for-debugportal/src/client/index.tsx index 259fbe0953..ac1de40123 100755 --- a/ui/packages/tidb-dashboard-for-debugportal/src/client/index.tsx +++ b/ui/packages/tidb-dashboard-for-debugportal/src/client/index.tsx @@ -2,7 +2,6 @@ import React from 'react' import i18next from 'i18next' import axios, { AxiosInstance } from 'axios' import { message, Modal, notification } from 'antd' -import * as singleSpa from 'single-spa' import { routing, i18n } from '@pingcap/tidb-dashboard-lib' import { @@ -10,8 +9,6 @@ import { DefaultApi as DashboardApi } from '@pingcap/tidb-dashboard-client' -import auth from '~/uilts/auth' - import translations from './translations' export * from '@pingcap/tidb-dashboard-client' @@ -69,8 +66,6 @@ function applyErrorHandlerInterceptor(instance: AxiosInstance) { if (!routing.isLocationMatch('/') && !routing.isSignInPage()) { message.error({ content, key: errCode }) } - auth.clearAuthToken() - singleSpa.navigateToUrl('#' + routing.signInRoute) err.handled = true } else if (handleError === 'default') { if (method === 'get') { diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.module.less b/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.module.less deleted file mode 100644 index faa1ea7527..0000000000 --- a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.module.less +++ /dev/null @@ -1,171 +0,0 @@ -@import 'antd/es/style/themes/default.less'; -@import 'antd/es/button/style/mixin.less'; - -@content-width: 400px; - -.container { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - display: flex; - flex-direction: row; - align-items: stretch; -} - -.contantContainer { - min-width: @content-width; - width: 38%; - background: #fff; - position: relative; -} - -.dialogContainer { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - padding: 30px 65px; - overflow-y: auto; -} - -.dialog { - max-width: 300px; - width: 100%; - margin: auto; -} - -.landingContainer { - flex-grow: 1; -} - -.landing { - background-size: cover; - background-position: center left; - height: 100%; -} - -.logo { - height: 40px; - margin-bottom: 80px; -} - -.signInButton { - margin-top: 10px; - margin-bottom: 20px; - font-size: 1rem; -} - -.extraLink { - font-size: 0.9rem; - margin: 15px 0; - a { - color: @gray-7; - &:hover { - color: @gray-7; - } - } - - &.clickable { - a:hover { - color: @link-hover-color; - } - } -} - -.alternativeFormLayer { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - background: #fff; - z-index: 2; -} - -.alternativeCloseButton { - font-size: 1rem; - border: 0; - background: #fff; - color: @text-color; - cursor: pointer; - padding: @padding-xss; - padding-right: 0; - - &:hover, - &:active, - &:focus { - color: @link-hover-color; - outline: 0; - } -} - -.alternativeButton { - .btn; - .btn-default; - - height: auto; - width: 100%; - color: @text-color; - - margin-bottom: @padding-sm; - text-align: left; - padding: @padding-md; - padding-right: 50px + @padding-md; - position: relative; - word-wrap: normal; - white-space: normal; - line-height: 1; - - &:hover, - &:active, - &:focus { - color: @text-color; - } - - .title { - color: @heading-color; - margin-bottom: @padding-sm; - } - - .icon { - font-size: 1.3rem; - position: absolute; - right: 0; - width: 50px; - text-align: center; - opacity: 0; - transform: translateX(-5px); - transition: opacity 0.2s linear, transform 0.2s linear; - color: @gray-6; - - // For vertical align - top: 50%; - margin-top: -20px; - line-height: 40px; - } - - &:hover, - &:active, - &:focus { - .icon { - opacity: 1; - transform: none; - } - } -} - -.container :global { - .formAnimation { - animation: 0.2s @ease-out-circ 0.5s antZoomBigIn; - animation-fill-mode: both; - animation-iteration-count: 1; - } - .landingAnimation { - animation: 0.5s linear 0 antFadeIn; - animation-fill-mode: both; - animation-iteration-count: 1; - } -} diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.tsx b/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.tsx deleted file mode 100755 index 1e5dae2e21..0000000000 --- a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/layout/signin/index.tsx +++ /dev/null @@ -1,545 +0,0 @@ -import CSSMotion from 'rc-animate/es/CSSMotion' -import cx from 'classnames' -import * as singleSpa from 'single-spa' -import React, { - useState, - useEffect, - useRef, - useCallback, - useMemo, - ReactNode -} from 'react' -import { - DownOutlined, - GlobalOutlined, - LockOutlined, - UserOutlined, - KeyOutlined, - ArrowRightOutlined, - CloseOutlined -} from '@ant-design/icons' -import { Form, Input, InputRef, Button, message, Typography, Modal } from 'antd' -import { useTranslation } from 'react-i18next' -import { useMount } from 'react-use' -import Flexbox from '@g07cha/flexbox-react' -import { useMemoizedFn } from 'ahooks' - -import { - // distro - isDistro, - // store - useIsFeatureSupport, - // components - Root, - AppearAnimate, - LanguageDropdown -} from '@pingcap/tidb-dashboard-lib' - -import client, { UserAuthenticateForm } from '~/client' -import auth from '~/uilts/auth' -import { getAuthURL } from '~/uilts/authSSO' -import { landingSvg, logoSvg } from '~/uilts/distro/assetsRes' - -import styles from './index.module.less' - -enum DisplayFormType { - uninitialized, - tidbCredential, - shareCode, - sso -} - -function AlternativeAuthLink({ onClick }) { - const { t } = useTranslation() - return ( -
- - {t('signin.form.use_alternative')} - -
- ) -} - -function LanguageDrop() { - return ( -
- - - Switch Language - - -
- ) -} - -interface IAlternativeFormButtonProps - extends React.ButtonHTMLAttributes { - title: string - description: string - className?: string -} - -function AlternativeFormButton({ - title, - description, - className, - ...restProps -}: IAlternativeFormButtonProps) { - return ( - - ) -} - -function AlternativeAuthForm({ - className, - onClose, - onSwitchForm, - supportedAuthTypes, - ...restProps -}) { - const { t } = useTranslation() - - return ( -
-
-
-
- -

- -
{t('signin.form.alternative.title')}
- -
-

-
- - onSwitchForm(DisplayFormType.tidbCredential)} - /> - - - onSwitchForm(DisplayFormType.shareCode)} - /> - - {Boolean(supportedAuthTypes.indexOf(auth.AuthTypes.SSO) > -1) && ( - - onSwitchForm(DisplayFormType.sso)} - /> - - )} - - -
-
-
- ) -} - -function useSignInSubmit( - successRoute, - fnLoginForm: (form) => UserAuthenticateForm, - onSuccess: (form) => void, - onFailure: () => void -) { - const { t } = useTranslation() - const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) - - const clearErrorMsg = useCallback(() => { - setError(null) - }, []) - - const handleSubmit = useMemoizedFn(async (form) => { - try { - clearErrorMsg() - setLoading(true) - const r = await client - .getInstance() - .userLogin({ message: fnLoginForm(form) }, { - handleError: 'custom' - } as any) - auth.setAuthToken(r.data.token) - message.success(t('signin.message.success')) - singleSpa.navigateToUrl(successRoute) - onSuccess(form) - } catch (e) { - const { handled, message, errCode } = e as any - if (!handled) { - const errMsg = t('signin.message.error', { msg: message }) - if (errCode !== 'api.user.signin.insufficient_priv') { - setError(errMsg) - } else { - // only add help link for TiDB distro when meeting insufficient_privileges error - const errComp = ( - <> - {errMsg} - {!isDistro() && ( - <> - {' '} - - {t('signin.message.access_doc')} - - - )} - - ) - setError(errComp) - } - onFailure() - } - } finally { - setLoading(false) - } - }) - - return { handleSubmit, loading, errorMsg: error, clearErrorMsg } -} - -const LAST_LOGIN_USERNAME_KEY = 'dashboard_last_login_username' - -function TiDBSignInForm({ successRoute, onClickAlternative }) { - const supportNonRootLogin = useIsFeatureSupport('nonRootLogin') - - const { t } = useTranslation() - - const [refForm] = Form.useForm() - const refPassword = useRef(null) - - const { handleSubmit, loading, errorMsg, clearErrorMsg } = useSignInSubmit( - successRoute, - (form) => ({ - username: form.username, - password: form.password, - type: auth.AuthTypes.SQLUser - }), - (form) => { - localStorage.setItem(LAST_LOGIN_USERNAME_KEY, form.username) - }, - () => { - refForm.setFieldsValue({ password: '' }) - setTimeout(() => { - refPassword.current?.focus() - }, 0) - } - ) - - useMount(() => { - refPassword?.current?.focus() - }) - - const lastLoginUsername = useMemo(() => { - return localStorage.getItem(LAST_LOGIN_USERNAME_KEY) || 'root' - }, []) - - return ( -
-
-
- - -

{t('signin.form.tidb_auth.title')}

-
- - } - disabled={!supportNonRootLogin} - /> - - - } - type="password" - disabled={loading} - onInput={clearErrorMsg} - ref={refPassword} - data-e2e="signin_password_input" - /> - - - - - - - -
-
- ) -} - -function CodeSignInForm({ successRoute, onClickAlternative }) { - const { t } = useTranslation() - - const [refForm] = Form.useForm() - const refPassword = useRef(null) - - const { handleSubmit, loading, errorMsg, clearErrorMsg } = useSignInSubmit( - successRoute, - (form) => ({ - password: form.code, - type: auth.AuthTypes.SharingCode - }), - () => {}, - () => { - refForm.setFieldsValue({ code: '' }) - setTimeout(() => { - refPassword.current?.focus() - }, 0) - } - ) - - useMount(() => { - refPassword?.current?.focus() - }) - - return ( -
-
-
- - -

{t('signin.form.code_auth.title')}

-
- - } - type="password" - onInput={clearErrorMsg} - disabled={loading} - ref={refPassword} - allowClear - /> - - - - - - - -
-
- ) -} - -function SSOSignInForm({ successRoute, onClickAlternative }) { - const { t } = useTranslation() - const [isLoading, setIsLoading] = useState(false) - - const handleSignIn = useCallback(async () => { - setIsLoading(true) - try { - const url = await getAuthURL() - window.location.href = url - // Do not hide loading status when url is resolved, since we are now jumping - } catch (e) { - setIsLoading(false) - } - }, []) - - return ( -
-
-
- - - - - - - -
-
- ) -} - -function App({ registry }) { - const successRoute = useMemo( - () => `#${registry.getDefaultRouter()}`, - [registry] - ) - const [alternativeVisible, setAlternativeVisible] = useState(false) - const [formType, setFormType] = useState(DisplayFormType.uninitialized) - const [supportedAuthTypes, setSupportedAuthTypes] = useState>([ - 0 - ]) - - const handleClickAlternative = useCallback(() => { - setAlternativeVisible(true) - }, []) - - const handleAlternativeClose = useCallback(() => { - setAlternativeVisible(false) - }, []) - - const handleSwitchForm = useCallback((k: DisplayFormType) => { - setFormType(k) - setAlternativeVisible(false) - }, []) - - useEffect(() => { - async function run() { - try { - const resp = await client.getInstance().userGetLoginInfo() - const loginInfo = resp.data - if ( - (loginInfo.supported_auth_types?.indexOf(auth.AuthTypes.SSO) ?? -1) > - -1 - ) { - setFormType(DisplayFormType.sso) - } else { - setFormType(DisplayFormType.tidbCredential) - } - setSupportedAuthTypes(loginInfo.supported_auth_types ?? []) - } catch (e) { - Modal.error({ - title: 'Initialize Sign in failed', - content: '' + e, - okText: 'Reload', - onOk: () => window.location.reload() - }) - } - } - run() - }, []) - - return ( - -
- - - {({ style, className }) => ( - - )} - - {formType === DisplayFormType.tidbCredential && ( - - )} - {formType === DisplayFormType.shareCode && ( - - )} - {formType === DisplayFormType.sso && ( - - )} - - -
- -
- - ) -} - -export default App diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/main.tsx b/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/main.tsx index 22f4db9ce6..37c74b511a 100755 --- a/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/main.tsx +++ b/ui/packages/tidb-dashboard-for-debugportal/src/dashboardApp/main.tsx @@ -19,7 +19,6 @@ import { } from '@pingcap/tidb-dashboard-lib' import { InfoInfoResponse, setupClient } from '~/client' -import auth from '~/uilts/auth' import { mustLoadAppInfo, reloadWhoAmI } from '~/uilts/store' import { AppOptions } from '~/uilts/appOptions' import AppRegistry from '~/uilts/registry' @@ -44,7 +43,6 @@ import AppOptimizerTrace from '~/apps/OptimizerTrace/meta' import AppDeadlock from '~/apps/Deadlock/meta' import LayoutMain from './layout/main' -import LayoutSignIn from './layout/signin' import translations from './layout/translations' @@ -142,15 +140,6 @@ async function webPageStart() { { registry } ) - singleSpa.registerApplication( - 'signin', - AppRegistry.newReactSpaApp(() => LayoutSignIn, 'root'), - () => { - return routing.isSignInPage() - }, - { registry } - ) - registry .register(AppUserProfile) .register(AppOverview) @@ -181,14 +170,6 @@ async function webPageStart() { // If there are auth errors, redirection will happen any way. So we continue. } - window.addEventListener('single-spa:app-change', () => { - if (!routing.isSignInPage()) { - if (!auth.getAuthTokenAsBearer()) { - singleSpa.navigateToUrl('#' + routing.signInRoute) - } - } - }) - window.addEventListener('single-spa:first-mount', () => { removeSpinner() }) diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/auth.ts b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/auth.ts deleted file mode 100644 index 088ea565ae..0000000000 --- a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/auth.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { EventEmitter2 } from 'eventemitter2' - -const tokenKey = 'dashboard_auth_token' - -export const authEvents = new EventEmitter2() - -export const EVENT_TOKEN_CHANGED = 'tokenChanged' - -export function getAuthToken() { - return localStorage.getItem(tokenKey) -} - -export function setAuthToken(token) { - const lastToken = getAuthToken() - if (lastToken !== token) { - localStorage.setItem(tokenKey, token) - authEvents.emit(EVENT_TOKEN_CHANGED, token) - } -} - -export function clearAuthToken() { - const lastToken = getAuthToken() - if (lastToken) { - localStorage.removeItem(tokenKey) - authEvents.emit(EVENT_TOKEN_CHANGED, null) - } -} - -export function getAuthTokenAsBearer() { - const token = getAuthToken() - if (!token) { - return null - } - return `Bearer ${token}` -} - -export enum AuthTypes { - SQLUser = 0, - SharingCode = 1, - SSO = 2 -} - -export default { - authEvents, - EVENT_TOKEN_CHANGED, - getAuthToken, - setAuthToken, - clearAuthToken, - getAuthTokenAsBearer, - AuthTypes -} diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/authSSO.ts b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/authSSO.ts deleted file mode 100644 index d871d34095..0000000000 --- a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/authSSO.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Modal } from 'antd' -import { ReqConfig } from '@pingcap/tidb-dashboard-lib' -import client from '~/client' -import { AuthTypes, setAuthToken } from './auth' - -function newRandomString(length: number) { - let text = '' - const possible = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' - for (let i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)) - } - return text -} - -function getBaseURL() { - return `${window.location.protocol}//${window.location.host}${window.location.pathname}` -} - -function getRedirectURL() { - return `${getBaseURL()}?sso_callback=1` -} - -export async function getAuthURL() { - const codeVerifier = newRandomString(128) - const state = newRandomString(32) - - sessionStorage.setItem('sso.codeVerifier', codeVerifier) - sessionStorage.setItem('sso.state', state) - const resp = await client - .getInstance() - .userSSOGetAuthURL({ codeVerifier, redirectUrl: getRedirectURL(), state }) - return resp.data -} - -export function isSSOCallback() { - const p = new URLSearchParams(window.location.search) - return p.has('sso_callback') -} - -async function handleSSOCallbackInner() { - const p = new URLSearchParams(window.location.search) - if (p.get('state') !== sessionStorage.getItem('sso.state')) { - throw new Error( - 'Invalid OIDC state: You may see this error when your SSO sign in is outdated.' - ) - } - const r = await client.getInstance().userLogin( - { - message: { - type: AuthTypes.SSO, - extra: JSON.stringify({ - code: p.get('code'), - code_verifier: sessionStorage.getItem('sso.codeVerifier'), - redirect_url: getRedirectURL() - }) - } - }, - { handleError: 'custom' } as ReqConfig - ) - - sessionStorage.removeItem('sso.codeVerifier') - sessionStorage.removeItem('sso.state') - - setAuthToken(r.data.token) - window.location.replace(getBaseURL()) -} - -export async function handleSSOCallback() { - try { - await handleSSOCallbackInner() - } catch (e) { - Modal.error({ - title: 'SSO Sign In Failed', - content: '' + e, - okText: 'Sign In Again', - onOk: () => window.location.replace(getBaseURL()) - }) - } -} diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/distro/assetsRes.ts b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/distro/assetsRes.ts index 83b906c61d..2a026275fe 100644 --- a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/distro/assetsRes.ts +++ b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/distro/assetsRes.ts @@ -1,7 +1,5 @@ import publicPathPrefix from '../publicPathPrefix' -const logoSvg = `${publicPathPrefix}/distro-res/logo.svg` const lightLogoSvg = `${publicPathPrefix}/distro-res/logo-icon-light.svg` -const landingSvg = `${publicPathPrefix}/distro-res/landing.png` -export { logoSvg, lightLogoSvg, landingSvg } +export { lightLogoSvg } diff --git a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/store.ts b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/store.ts index ec277d4f0d..1ff1789ec1 100644 --- a/ui/packages/tidb-dashboard-for-debugportal/src/uilts/store.ts +++ b/ui/packages/tidb-dashboard-for-debugportal/src/uilts/store.ts @@ -1,16 +1,7 @@ import { store, ReqConfig } from '@pingcap/tidb-dashboard-lib' import client, { InfoInfoResponse } from '~/client' -import { authEvents, EVENT_TOKEN_CHANGED, getAuthToken } from './auth' - export async function reloadWhoAmI(): Promise { - if (!getAuthToken()) { - store.update((s) => { - s.whoAmI = undefined - }) - return false - } - try { const resp = await client.getInstance().infoWhoami({ handleError: 'custom' @@ -36,7 +27,3 @@ export async function mustLoadAppInfo(): Promise { }) return resp.data } - -authEvents.on(EVENT_TOKEN_CHANGED, async () => { - await reloadWhoAmI() -})