From 0de62d4ee8a47c076cf3f52d19330bd9f3b14574 Mon Sep 17 00:00:00 2001 From: Elena Makarova Date: Fri, 21 Feb 2025 17:44:06 +0300 Subject: [PATCH 1/3] feat: add database to authentication process --- src/containers/App/Content.tsx | 24 +++++++++++---- .../YdbInternalUser/YdbInternalUser.tsx | 7 ++++- .../Authentication/Authentication.tsx | 30 +++++++++++++++++-- src/containers/Authentication/utils.ts | 3 ++ src/services/api/auth.ts | 2 +- .../reducers/authentication/authentication.ts | 5 +++- .../reducers/capabilities/capabilities.ts | 16 +++++++--- src/store/reducers/capabilities/hooks.ts | 23 ++++++++++++-- src/types/api/capabilities.ts | 5 ++++ 9 files changed, 98 insertions(+), 17 deletions(-) diff --git a/src/containers/App/Content.tsx b/src/containers/App/Content.tsx index e24803b45..5f1de3126 100644 --- a/src/containers/App/Content.tsx +++ b/src/containers/App/Content.tsx @@ -4,6 +4,7 @@ import {connect} from 'react-redux'; import type {RedirectProps} from 'react-router-dom'; import {Redirect, Route, Switch} from 'react-router-dom'; +import {AccessDenied} from '../../components/Errors/403'; import {PageError} from '../../components/Errors/PageError/PageError'; import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper'; import {useSlots} from '../../components/slots'; @@ -12,7 +13,11 @@ import type {SlotComponent} from '../../components/slots/types'; import routes from '../../routes'; import type {RootState} from '../../store'; import {authenticationApi} from '../../store/reducers/authentication/authentication'; -import {useCapabilitiesLoaded, useCapabilitiesQuery} from '../../store/reducers/capabilities/hooks'; +import { + useAccessTotallyRestricted, + useCapabilitiesLoaded, + useCapabilitiesQuery, +} from '../../store/reducers/capabilities/hooks'; import {nodesListApi} from '../../store/reducers/nodesList'; import {cn} from '../../utils/cn'; import {useDatabaseFromQuery} from '../../utils/hooks/useDatabaseFromQuery'; @@ -177,10 +182,14 @@ export function Content(props: ContentProps) { function DataWrapper({children}: {children: React.ReactNode}) { return ( - - - {children} - + // capabilities is going to work without authentication, but not all running systems are supporting this yet + + + + {/* this GetCapabilities will be removed */} + {children} + + ); } @@ -219,6 +228,11 @@ interface ContentWrapperProps { function ContentWrapper(props: ContentWrapperProps) { const {singleClusterMode, isAuthenticated} = props; + const isForbidded = useAccessTotallyRestricted(); + + if (isForbidded) { + return ; + } return ( diff --git a/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx b/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx index 6c7397d27..9efe88d35 100644 --- a/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx +++ b/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx @@ -5,6 +5,7 @@ import {useHistory} from 'react-router-dom'; import routes, {createHref} from '../../../routes'; import {authenticationApi} from '../../../store/reducers/authentication/authentication'; import {cn} from '../../../utils/cn'; +import {useDatabaseFromQuery} from '../../../utils/hooks/useDatabaseFromQuery'; import i18n from '../i18n'; import './YdbInternalUser.scss'; @@ -13,11 +14,15 @@ const b = cn('kv-ydb-internal-user'); export function YdbInternalUser({login}: {login?: string}) { const [logout] = authenticationApi.useLogoutMutation(); + const database = useDatabaseFromQuery(); const history = useHistory(); const handleLoginClick = () => { history.push( - createHref(routes.auth, undefined, {returnUrl: encodeURIComponent(location.href)}), + createHref(routes.auth, undefined, { + returnUrl: encodeURIComponent(location.href), + database, + }), ); }; diff --git a/src/containers/Authentication/Authentication.tsx b/src/containers/Authentication/Authentication.tsx index fbee833cf..5f87792b4 100644 --- a/src/containers/Authentication/Authentication.tsx +++ b/src/containers/Authentication/Authentication.tsx @@ -6,9 +6,10 @@ import {useHistory, useLocation} from 'react-router-dom'; import {parseQuery} from '../../routes'; import {authenticationApi} from '../../store/reducers/authentication/authentication'; +import {useLoginWithDatabase} from '../../store/reducers/capabilities/hooks'; import {cn} from '../../utils/cn'; -import {isPasswordError, isUserError} from './utils'; +import {isDatabaseError, isPasswordError, isUserError} from './utils'; import ydbLogoIcon from '../../assets/icons/ydb.svg'; @@ -24,20 +25,28 @@ function Authentication({closable = false}: AuthenticationProps) { const history = useHistory(); const location = useLocation(); + const needDatabase = useLoginWithDatabase(); + const [authenticate, {isLoading}] = authenticationApi.useAuthenticateMutation(undefined); - const {returnUrl} = parseQuery(location); + const {returnUrl, database: databaseFromQuery} = parseQuery(location); const [login, setLogin] = React.useState(''); + const [database, setDatabase] = React.useState(databaseFromQuery?.toString() ?? ''); const [password, setPass] = React.useState(''); const [loginError, setLoginError] = React.useState(''); const [passwordError, setPasswordError] = React.useState(''); + const [databaseError, setDatabaseError] = React.useState(''); const [showPassword, setShowPassword] = React.useState(false); const onLoginUpdate = (value: string) => { setLogin(value); setLoginError(''); }; + const onDatabaseUpdate = (value: string) => { + setDatabase(value); + setDatabaseError(''); + }; const onPassUpdate = (value: string) => { setPass(value); @@ -45,7 +54,7 @@ function Authentication({closable = false}: AuthenticationProps) { }; const onLoginClick = () => { - authenticate({user: login, password}) + authenticate({user: login, password, database}) .unwrap() .then(() => { if (returnUrl) { @@ -66,6 +75,9 @@ function Authentication({closable = false}: AuthenticationProps) { if (isPasswordError(error)) { setPasswordError(error.data.error); } + if (isDatabaseError(error)) { + setDatabaseError(error.data.error); + } }); }; @@ -125,6 +137,18 @@ function Authentication({closable = false}: AuthenticationProps) { + {needDatabase && ( +
+ +
+ )} + ); + }; + return (
@@ -41,13 +54,7 @@ export function YdbInternalUser({login}: {login?: string}) { ) : ( - + renderLoginButton() )}
); From a66cff553edf69c60b4a6e77fb6296b7d9cb8524 Mon Sep 17 00:00:00 2001 From: Elena Makarova Date: Mon, 24 Feb 2025 12:49:42 +0300 Subject: [PATCH 3/3] fix: review --- src/containers/App/Content.tsx | 8 ++++---- .../AsideNavigation/YdbInternalUser/YdbInternalUser.tsx | 6 +++--- src/store/reducers/capabilities/hooks.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/containers/App/Content.tsx b/src/containers/App/Content.tsx index f0632a189..82741845c 100644 --- a/src/containers/App/Content.tsx +++ b/src/containers/App/Content.tsx @@ -14,9 +14,9 @@ import routes from '../../routes'; import type {RootState} from '../../store'; import {authenticationApi} from '../../store/reducers/authentication/authentication'; import { - useAccessTotallyRestricted, useCapabilitiesLoaded, useCapabilitiesQuery, + useClusterWithoutAuthInUI, } from '../../store/reducers/capabilities/hooks'; import {nodesListApi} from '../../store/reducers/nodesList'; import {cn} from '../../utils/cn'; @@ -228,10 +228,10 @@ interface ContentWrapperProps { function ContentWrapper(props: ContentWrapperProps) { const {singleClusterMode, isAuthenticated} = props; - const isForbidded = useAccessTotallyRestricted(); + const authUnavailable = useClusterWithoutAuthInUI(); const renderNotAuthenticated = () => { - if (isForbidded) { + if (authUnavailable) { return ; } return ; @@ -239,7 +239,7 @@ function ContentWrapper(props: ContentWrapperProps) { return ( - {!isForbidded && ( + {!authUnavailable && ( diff --git a/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx b/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx index d05e42076..c0490b467 100644 --- a/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx +++ b/src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx @@ -4,7 +4,7 @@ import {useHistory} from 'react-router-dom'; import routes, {createHref} from '../../../routes'; import {authenticationApi} from '../../../store/reducers/authentication/authentication'; -import {useAccessTotallyRestricted} from '../../../store/reducers/capabilities/hooks'; +import {useClusterWithoutAuthInUI} from '../../../store/reducers/capabilities/hooks'; import {cn} from '../../../utils/cn'; import {useDatabaseFromQuery} from '../../../utils/hooks/useDatabaseFromQuery'; import i18n from '../i18n'; @@ -15,7 +15,7 @@ const b = cn('kv-ydb-internal-user'); export function YdbInternalUser({login}: {login?: string}) { const [logout] = authenticationApi.useLogoutMutation(); - const isForbidded = useAccessTotallyRestricted(); + const authUnavailable = useClusterWithoutAuthInUI(); const database = useDatabaseFromQuery(); const history = useHistory(); @@ -33,7 +33,7 @@ export function YdbInternalUser({login}: {login?: string}) { }; const renderLoginButton = () => { - if (isForbidded) { + if (authUnavailable) { return null; } return ( diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts index 2a5ed473b..80609f301 100644 --- a/src/store/reducers/capabilities/hooks.ts +++ b/src/store/reducers/capabilities/hooks.ts @@ -80,7 +80,7 @@ const useGetSecuritySetting = (feature: SecuritySetting) => { return useTypedSelector((state) => selectSecuritySetting(state, feature, database)); }; -export const useAccessTotallyRestricted = () => { +export const useClusterWithoutAuthInUI = () => { return useGetSecuritySetting('UseLoginProvider') === false; };