From 02256eac4b7d4f4b20bf5b8965442408a5057f8e Mon Sep 17 00:00:00 2001 From: Elena Makarova Date: Mon, 4 Aug 2025 17:42:56 +0300 Subject: [PATCH 1/2] feat: add new role IsDatabaseAllowed and shrink breadcrumbs --- src/containers/Header/Header.tsx | 10 +++++++--- src/containers/Header/breadcrumbs.tsx | 11 +++++++++-- src/store/reducers/authentication/authentication.ts | 8 ++++++-- src/store/reducers/authentication/types.ts | 1 + src/store/reducers/header/types.ts | 4 +++- src/types/api/whoami.ts | 2 ++ src/utils/hooks/useIsUserAllowedToMakeChanges.ts | 8 +++++++- 7 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/containers/Header/Header.tsx b/src/containers/Header/Header.tsx index 94d767797d..dbff2c68c1 100644 --- a/src/containers/Header/Header.tsx +++ b/src/containers/Header/Header.tsx @@ -14,7 +14,10 @@ import {DEVELOPER_UI_TITLE} from '../../utils/constants'; import {createDeveloperUIInternalPageHref} from '../../utils/developerUI/developerUI'; import {useTypedSelector} from '../../utils/hooks'; import {useDatabaseFromQuery} from '../../utils/hooks/useDatabaseFromQuery'; -import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges'; +import { + useIsOnlyDatabaseUser, + useIsUserAllowedToMakeChanges, +} from '../../utils/hooks/useIsUserAllowedToMakeChanges'; import {getBreadcrumbs} from './breadcrumbs'; import {headerKeyset} from './i18n'; @@ -27,6 +30,7 @@ function Header() { const {page, pageBreadcrumbsOptions} = useTypedSelector((state) => state.header); const singleClusterMode = useTypedSelector((state) => state.singleClusterMode); const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); + const isOnlyDatabaseUser = useIsOnlyDatabaseUser(); const {title: clusterTitle} = useClusterBaseInfo(); @@ -39,7 +43,7 @@ function Header() { useAddClusterFeatureAvailable() && uiFactory.onAddCluster !== undefined; const breadcrumbItems = React.useMemo(() => { - let options = {...pageBreadcrumbsOptions, singleClusterMode}; + let options = {...pageBreadcrumbsOptions, singleClusterMode, isOnlyDatabaseUser}; if (clusterTitle) { options = { @@ -53,7 +57,7 @@ function Header() { return breadcrumbs.map((item) => { return {...item, action: () => {}}; }); - }, [clusterTitle, page, pageBreadcrumbsOptions, singleClusterMode]); + }, [clusterTitle, page, pageBreadcrumbsOptions, singleClusterMode, isOnlyDatabaseUser]); const renderRightControls = () => { const elements: React.ReactNode[] = []; diff --git a/src/containers/Header/breadcrumbs.tsx b/src/containers/Header/breadcrumbs.tsx index e295e60532..3cdbccd37f 100644 --- a/src/containers/Header/breadcrumbs.tsx +++ b/src/containers/Header/breadcrumbs.tsx @@ -46,7 +46,10 @@ const getQueryForTenant = (type: 'nodes' | 'tablets') => ({ [TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS[type], }); -const getClustersBreadcrumbs: GetBreadcrumbs = () => { +const getClustersBreadcrumbs: GetBreadcrumbs = (options) => { + if (options.isOnlyDatabaseUser) { + return []; + } return [ { text: headerKeyset('breadcrumbs.clusters'), @@ -56,7 +59,11 @@ const getClustersBreadcrumbs: GetBreadcrumbs = () => }; const getClusterBreadcrumbs: GetBreadcrumbs = (options, query = {}) => { - const {clusterName, clusterTab, singleClusterMode} = options; + const {clusterName, clusterTab, singleClusterMode, isOnlyDatabaseUser} = options; + + if (isOnlyDatabaseUser) { + return []; + } let breadcrumbs: RawBreadcrumbItem[] = []; diff --git a/src/store/reducers/authentication/authentication.ts b/src/store/reducers/authentication/authentication.ts index 460ebef8a9..2872e48510 100644 --- a/src/store/reducers/authentication/authentication.ts +++ b/src/store/reducers/authentication/authentication.ts @@ -26,7 +26,8 @@ export const slice = createSlice({ } }, setUser: (state, action: PayloadAction) => { - const {UserSID, AuthType, IsMonitoringAllowed} = action.payload; + const {UserSID, AuthType, IsMonitoringAllowed, IsDatabaseAllowed, IsViewerAllowed} = + action.payload; state.user = AuthType === 'Login' ? UserSID : undefined; @@ -35,17 +36,20 @@ export const slice = createSlice({ // Otherwise every user is allowed to make changes // Anyway there will be guards on backend state.isUserAllowedToMakeChanges = IsMonitoringAllowed !== false; + state.isOnlyDatabaseUser = IsDatabaseAllowed && IsViewerAllowed === false; }, }, selectors: { selectIsUserAllowedToMakeChanges: (state) => state.isUserAllowedToMakeChanges, + selectIsOnlyDatabaseUser: (state) => state.isOnlyDatabaseUser, selectUser: (state) => state.user, }, }); export default slice.reducer; export const {setIsAuthenticated, setUser} = slice.actions; -export const {selectIsUserAllowedToMakeChanges, selectUser} = slice.selectors; +export const {selectIsUserAllowedToMakeChanges, selectIsOnlyDatabaseUser, selectUser} = + slice.selectors; export const authenticationApi = api.injectEndpoints({ endpoints: (build) => ({ diff --git a/src/store/reducers/authentication/types.ts b/src/store/reducers/authentication/types.ts index bb58438c44..8fa12cf4ae 100644 --- a/src/store/reducers/authentication/types.ts +++ b/src/store/reducers/authentication/types.ts @@ -1,5 +1,6 @@ export interface AuthenticationState { isAuthenticated: boolean; isUserAllowedToMakeChanges?: boolean; + isOnlyDatabaseUser?: boolean; user: string | undefined; } diff --git a/src/store/reducers/header/types.ts b/src/store/reducers/header/types.ts index c1f1ce60a2..7550c841bc 100644 --- a/src/store/reducers/header/types.ts +++ b/src/store/reducers/header/types.ts @@ -15,7 +15,9 @@ export type Page = | 'storageGroup' | undefined; -export interface ClustersBreadcrumbsOptions {} +export interface ClustersBreadcrumbsOptions { + isOnlyDatabaseUser?: boolean; +} export interface ClusterBreadcrumbsOptions extends ClustersBreadcrumbsOptions { clusterName?: string; diff --git a/src/types/api/whoami.ts b/src/types/api/whoami.ts index 9b5cd52042..ce3dae27f5 100644 --- a/src/types/api/whoami.ts +++ b/src/types/api/whoami.ts @@ -9,6 +9,8 @@ export interface TUserToken { OriginalUserToken?: string; AuthType?: string; + /** Is user allowed to view only database specific data */ + IsDatabaseAllowed?: boolean; /** Is user allowed to view data */ IsViewerAllowed?: boolean; /** Is user allowed to view deeper and make simple changes */ diff --git a/src/utils/hooks/useIsUserAllowedToMakeChanges.ts b/src/utils/hooks/useIsUserAllowedToMakeChanges.ts index cb4b160256..cdc7e1312a 100644 --- a/src/utils/hooks/useIsUserAllowedToMakeChanges.ts +++ b/src/utils/hooks/useIsUserAllowedToMakeChanges.ts @@ -1,7 +1,13 @@ -import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication'; +import { + selectIsOnlyDatabaseUser, + selectIsUserAllowedToMakeChanges, +} from '../../store/reducers/authentication/authentication'; import {useTypedSelector} from './useTypedSelector'; export function useIsUserAllowedToMakeChanges() { return useTypedSelector(selectIsUserAllowedToMakeChanges); } +export function useIsOnlyDatabaseUser() { + return useTypedSelector(selectIsOnlyDatabaseUser); +} From f6d277df4fabd810877bb2441e4a3001b0df45c6 Mon Sep 17 00:00:00 2001 From: Elena Makarova Date: Tue, 5 Aug 2025 11:18:16 +0300 Subject: [PATCH 2/2] fixes --- src/containers/Header/Header.tsx | 12 ++++++++---- src/containers/Header/breadcrumbs.tsx | 6 +++--- src/store/reducers/authentication/authentication.ts | 10 ++++------ src/store/reducers/authentication/types.ts | 2 +- src/store/reducers/header/types.ts | 2 +- src/types/api/whoami.ts | 2 +- src/utils/hooks/useIsUserAllowedToMakeChanges.ts | 6 +++--- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/containers/Header/Header.tsx b/src/containers/Header/Header.tsx index dbff2c68c1..e6a4a59394 100644 --- a/src/containers/Header/Header.tsx +++ b/src/containers/Header/Header.tsx @@ -15,8 +15,8 @@ import {createDeveloperUIInternalPageHref} from '../../utils/developerUI/develop import {useTypedSelector} from '../../utils/hooks'; import {useDatabaseFromQuery} from '../../utils/hooks/useDatabaseFromQuery'; import { - useIsOnlyDatabaseUser, useIsUserAllowedToMakeChanges, + useIsViewerUser, } from '../../utils/hooks/useIsUserAllowedToMakeChanges'; import {getBreadcrumbs} from './breadcrumbs'; @@ -30,7 +30,7 @@ function Header() { const {page, pageBreadcrumbsOptions} = useTypedSelector((state) => state.header); const singleClusterMode = useTypedSelector((state) => state.singleClusterMode); const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); - const isOnlyDatabaseUser = useIsOnlyDatabaseUser(); + const isViewerUser = useIsViewerUser(); const {title: clusterTitle} = useClusterBaseInfo(); @@ -43,7 +43,11 @@ function Header() { useAddClusterFeatureAvailable() && uiFactory.onAddCluster !== undefined; const breadcrumbItems = React.useMemo(() => { - let options = {...pageBreadcrumbsOptions, singleClusterMode, isOnlyDatabaseUser}; + let options = { + ...pageBreadcrumbsOptions, + singleClusterMode, + isViewerUser, + }; if (clusterTitle) { options = { @@ -57,7 +61,7 @@ function Header() { return breadcrumbs.map((item) => { return {...item, action: () => {}}; }); - }, [clusterTitle, page, pageBreadcrumbsOptions, singleClusterMode, isOnlyDatabaseUser]); + }, [clusterTitle, page, pageBreadcrumbsOptions, singleClusterMode, isViewerUser]); const renderRightControls = () => { const elements: React.ReactNode[] = []; diff --git a/src/containers/Header/breadcrumbs.tsx b/src/containers/Header/breadcrumbs.tsx index 3cdbccd37f..07be45b450 100644 --- a/src/containers/Header/breadcrumbs.tsx +++ b/src/containers/Header/breadcrumbs.tsx @@ -47,7 +47,7 @@ const getQueryForTenant = (type: 'nodes' | 'tablets') => ({ }); const getClustersBreadcrumbs: GetBreadcrumbs = (options) => { - if (options.isOnlyDatabaseUser) { + if (!options.isViewerUser) { return []; } return [ @@ -59,9 +59,9 @@ const getClustersBreadcrumbs: GetBreadcrumbs = (opti }; const getClusterBreadcrumbs: GetBreadcrumbs = (options, query = {}) => { - const {clusterName, clusterTab, singleClusterMode, isOnlyDatabaseUser} = options; + const {clusterName, clusterTab, singleClusterMode, isViewerUser} = options; - if (isOnlyDatabaseUser) { + if (!isViewerUser) { return []; } diff --git a/src/store/reducers/authentication/authentication.ts b/src/store/reducers/authentication/authentication.ts index 2872e48510..ee9f307e57 100644 --- a/src/store/reducers/authentication/authentication.ts +++ b/src/store/reducers/authentication/authentication.ts @@ -26,8 +26,7 @@ export const slice = createSlice({ } }, setUser: (state, action: PayloadAction) => { - const {UserSID, AuthType, IsMonitoringAllowed, IsDatabaseAllowed, IsViewerAllowed} = - action.payload; + const {UserSID, AuthType, IsMonitoringAllowed, IsViewerAllowed} = action.payload; state.user = AuthType === 'Login' ? UserSID : undefined; @@ -36,20 +35,19 @@ export const slice = createSlice({ // Otherwise every user is allowed to make changes // Anyway there will be guards on backend state.isUserAllowedToMakeChanges = IsMonitoringAllowed !== false; - state.isOnlyDatabaseUser = IsDatabaseAllowed && IsViewerAllowed === false; + state.isViewerUser = IsViewerAllowed; }, }, selectors: { selectIsUserAllowedToMakeChanges: (state) => state.isUserAllowedToMakeChanges, - selectIsOnlyDatabaseUser: (state) => state.isOnlyDatabaseUser, + selectIsViewerUser: (state) => state.isViewerUser, selectUser: (state) => state.user, }, }); export default slice.reducer; export const {setIsAuthenticated, setUser} = slice.actions; -export const {selectIsUserAllowedToMakeChanges, selectIsOnlyDatabaseUser, selectUser} = - slice.selectors; +export const {selectIsUserAllowedToMakeChanges, selectIsViewerUser, selectUser} = slice.selectors; export const authenticationApi = api.injectEndpoints({ endpoints: (build) => ({ diff --git a/src/store/reducers/authentication/types.ts b/src/store/reducers/authentication/types.ts index 8fa12cf4ae..ba2218bf67 100644 --- a/src/store/reducers/authentication/types.ts +++ b/src/store/reducers/authentication/types.ts @@ -1,6 +1,6 @@ export interface AuthenticationState { isAuthenticated: boolean; isUserAllowedToMakeChanges?: boolean; - isOnlyDatabaseUser?: boolean; + isViewerUser?: boolean; user: string | undefined; } diff --git a/src/store/reducers/header/types.ts b/src/store/reducers/header/types.ts index 7550c841bc..009b2dfc3c 100644 --- a/src/store/reducers/header/types.ts +++ b/src/store/reducers/header/types.ts @@ -16,7 +16,7 @@ export type Page = | undefined; export interface ClustersBreadcrumbsOptions { - isOnlyDatabaseUser?: boolean; + isViewerUser?: boolean; } export interface ClusterBreadcrumbsOptions extends ClustersBreadcrumbsOptions { diff --git a/src/types/api/whoami.ts b/src/types/api/whoami.ts index ce3dae27f5..7313e4c5de 100644 --- a/src/types/api/whoami.ts +++ b/src/types/api/whoami.ts @@ -11,7 +11,7 @@ export interface TUserToken { /** Is user allowed to view only database specific data */ IsDatabaseAllowed?: boolean; - /** Is user allowed to view data */ + /** Is user allowed to view data (includes IsDatabaseAllowed rights) */ IsViewerAllowed?: boolean; /** Is user allowed to view deeper and make simple changes */ IsMonitoringAllowed?: boolean; diff --git a/src/utils/hooks/useIsUserAllowedToMakeChanges.ts b/src/utils/hooks/useIsUserAllowedToMakeChanges.ts index cdc7e1312a..7e7d9e0390 100644 --- a/src/utils/hooks/useIsUserAllowedToMakeChanges.ts +++ b/src/utils/hooks/useIsUserAllowedToMakeChanges.ts @@ -1,6 +1,6 @@ import { - selectIsOnlyDatabaseUser, selectIsUserAllowedToMakeChanges, + selectIsViewerUser, } from '../../store/reducers/authentication/authentication'; import {useTypedSelector} from './useTypedSelector'; @@ -8,6 +8,6 @@ import {useTypedSelector} from './useTypedSelector'; export function useIsUserAllowedToMakeChanges() { return useTypedSelector(selectIsUserAllowedToMakeChanges); } -export function useIsOnlyDatabaseUser() { - return useTypedSelector(selectIsOnlyDatabaseUser); +export function useIsViewerUser() { + return useTypedSelector(selectIsViewerUser); }