Skip to content

Commit fae206e

Browse files
authored
feat: restrictions for users without IsViewerAllowed role (#2675)
1 parent e5c5e9d commit fae206e

File tree

13 files changed

+153
-42
lines changed

13 files changed

+153
-42
lines changed

src/components/VDiskPopup/VDiskPopup.tsx

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
1313
import {isFullVDiskData} from '../../utils/disks/helpers';
1414
import type {PreparedVDisk, UnavailableDonor} from '../../utils/disks/types';
1515
import {useTypedSelector} from '../../utils/hooks';
16-
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
16+
import {
17+
useIsUserAllowedToMakeChanges,
18+
useIsViewerUser,
19+
} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
1720
import {bytesToGB, bytesToSpeed} from '../../utils/utils';
1821
import type {InfoViewerItem} from '../InfoViewer';
1922
import {InfoViewer} from '../InfoViewer';
@@ -23,23 +26,27 @@ import {preparePDiskData} from '../PDiskPopup/PDiskPopup';
2326
import {getVDiskLink} from '../VDisk/utils';
2427
import {vDiskInfoKeyset} from '../VDiskInfo/i18n';
2528

29+
import {vDiskPopupKeyset} from './i18n';
30+
2631
import './VDiskPopup.scss';
2732

2833
const b = cn('vdisk-storage-popup');
2934

3035
const prepareUnavailableVDiskData = (data: UnavailableDonor, withDeveloperUILink?: boolean) => {
3136
const {NodeId, PDiskId, VSlotId, StoragePoolName} = data;
3237

33-
const vdiskData: InfoViewerItem[] = [{label: 'State', value: 'not available'}];
38+
const vdiskData: InfoViewerItem[] = [
39+
{label: vDiskPopupKeyset('label_state'), value: vDiskPopupKeyset('context_not-available')},
40+
];
3441

3542
if (StoragePoolName) {
36-
vdiskData.push({label: 'StoragePool', value: StoragePoolName});
43+
vdiskData.push({label: vDiskPopupKeyset('label_storage-pool'), value: StoragePoolName});
3744
}
3845

3946
vdiskData.push(
40-
{label: 'NodeId', value: NodeId ?? EMPTY_DATA_PLACEHOLDER},
41-
{label: 'PDiskId', value: PDiskId ?? EMPTY_DATA_PLACEHOLDER},
42-
{label: 'VSlotId', value: VSlotId ?? EMPTY_DATA_PLACEHOLDER},
47+
{label: vDiskPopupKeyset('label_node-id'), value: NodeId ?? EMPTY_DATA_PLACEHOLDER},
48+
{label: vDiskPopupKeyset('label_pdisk-id'), value: PDiskId ?? EMPTY_DATA_PLACEHOLDER},
49+
{label: vDiskPopupKeyset('label_vslot-id'), value: VSlotId ?? EMPTY_DATA_PLACEHOLDER},
4350
);
4451

4552
if (
@@ -55,7 +62,7 @@ const prepareUnavailableVDiskData = (data: UnavailableDonor, withDeveloperUILink
5562
});
5663

5764
vdiskData.push({
58-
label: 'Links',
65+
label: vDiskPopupKeyset('label_links'),
5966
value: <LinkWithIcon title={'Developer UI'} url={vDiskInternalViewerPath} />,
6067
});
6168
}
@@ -85,58 +92,61 @@ const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) =>
8592
} = data;
8693

8794
const vdiskData: InfoViewerItem[] = [
88-
{label: 'VDisk', value: StringifiedId},
89-
{label: 'State', value: VDiskState ?? 'not available'},
95+
{label: vDiskPopupKeyset('label_vdisk'), value: StringifiedId},
96+
{
97+
label: vDiskPopupKeyset('label_state'),
98+
value: VDiskState ?? vDiskPopupKeyset('context_not-available'),
99+
},
90100
];
91101

92102
if (StoragePoolName) {
93-
vdiskData.push({label: 'StoragePool', value: StoragePoolName});
103+
vdiskData.push({label: vDiskPopupKeyset('label_storage-pool'), value: StoragePoolName});
94104
}
95105

96106
if (SatisfactionRank && SatisfactionRank.FreshRank?.Flag !== EFlag.Green) {
97107
vdiskData.push({
98-
label: 'Fresh',
108+
label: vDiskPopupKeyset('label_fresh'),
99109
value: SatisfactionRank.FreshRank?.Flag,
100110
});
101111
}
102112

103113
if (SatisfactionRank && SatisfactionRank.LevelRank?.Flag !== EFlag.Green) {
104114
vdiskData.push({
105-
label: 'Level',
115+
label: vDiskPopupKeyset('label_level'),
106116
value: SatisfactionRank.LevelRank?.Flag,
107117
});
108118
}
109119

110120
if (SatisfactionRank && SatisfactionRank.FreshRank?.RankPercent) {
111121
vdiskData.push({
112-
label: 'Fresh',
122+
label: vDiskPopupKeyset('label_fresh'),
113123
value: SatisfactionRank.FreshRank.RankPercent,
114124
});
115125
}
116126

117127
if (SatisfactionRank && SatisfactionRank.LevelRank?.RankPercent) {
118128
vdiskData.push({
119-
label: 'Level',
129+
label: vDiskPopupKeyset('label_level'),
120130
value: SatisfactionRank.LevelRank.RankPercent,
121131
});
122132
}
123133

124134
if (DiskSpace && DiskSpace !== EFlag.Green) {
125-
vdiskData.push({label: 'Space', value: DiskSpace});
135+
vdiskData.push({label: vDiskPopupKeyset('label_space'), value: DiskSpace});
126136
}
127137

128138
if (FrontQueues && FrontQueues !== EFlag.Green) {
129-
vdiskData.push({label: 'FrontQueues', value: FrontQueues});
139+
vdiskData.push({label: vDiskPopupKeyset('label_front-queues'), value: FrontQueues});
130140
}
131141

132142
if (Replicated === false && VDiskState === EVDiskState.OK) {
133-
vdiskData.push({label: 'Replicated', value: 'NO'});
143+
vdiskData.push({label: vDiskPopupKeyset('label_replicated'), value: 'NO'});
134144

135145
// Only show replication progress and time remaining when disk is not replicated and state is OK
136146
if (valueIsDefined(ReplicationProgress)) {
137147
const progressPercent = Math.round(ReplicationProgress * 100);
138148
vdiskData.push({
139-
label: 'Progress',
149+
label: vDiskPopupKeyset('label_progress'),
140150
value: `${progressPercent}%`,
141151
});
142152
}
@@ -145,31 +155,34 @@ const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) =>
145155
const timeRemaining = formatUptimeInSeconds(ReplicationSecondsRemaining);
146156
if (timeRemaining) {
147157
vdiskData.push({
148-
label: 'Remaining',
158+
label: vDiskPopupKeyset('label_remaining'),
149159
value: timeRemaining,
150160
});
151161
}
152162
}
153163
}
154164

155165
if (UnsyncedVDisks) {
156-
vdiskData.push({label: 'UnsyncVDisks', value: UnsyncedVDisks});
166+
vdiskData.push({label: vDiskPopupKeyset('label_unsync-vdisks'), value: UnsyncedVDisks});
157167
}
158168

159169
if (Number(AllocatedSize)) {
160170
vdiskData.push({
161-
label: 'Allocated',
171+
label: vDiskPopupKeyset('label_allocated'),
162172
value: bytesToGB(AllocatedSize),
163173
});
164174
}
165175

166176
if (Number(ReadThroughput)) {
167-
vdiskData.push({label: 'Read', value: bytesToSpeed(ReadThroughput)});
177+
vdiskData.push({
178+
label: vDiskPopupKeyset('label_read'),
179+
value: bytesToSpeed(ReadThroughput),
180+
});
168181
}
169182

170183
if (Number(WriteThroughput)) {
171184
vdiskData.push({
172-
label: 'Write',
185+
label: vDiskPopupKeyset('label_write'),
173186
value: bytesToSpeed(WriteThroughput),
174187
});
175188
}
@@ -191,7 +204,7 @@ const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) =>
191204
const vDiskPagePath = getVDiskLink({VDiskSlotId, PDiskId, NodeId, StringifiedId});
192205
if (vDiskPagePath) {
193206
vdiskData.push({
194-
label: 'Links',
207+
label: vDiskPopupKeyset('label_links'),
195208
value: (
196209
<Flex wrap="wrap" gap={2}>
197210
<LinkWithIcon
@@ -221,6 +234,7 @@ interface VDiskPopupProps {
221234

222235
export const VDiskPopup = ({data}: VDiskPopupProps) => {
223236
const isFullData = isFullVDiskData(data);
237+
const isViewerUser = useIsViewerUser();
224238

225239
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
226240

@@ -247,7 +261,7 @@ export const VDiskPopup = ({data}: VDiskPopupProps) => {
247261
const donors = data.Donors;
248262
for (const donor of donors) {
249263
donorsInfo.push({
250-
label: 'VDisk',
264+
label: vDiskPopupKeyset('label_vdisk'),
251265
value: <InternalLink to={getVDiskLink(donor)}>{donor.StringifiedId}</InternalLink>,
252266
});
253267
}
@@ -257,7 +271,7 @@ export const VDiskPopup = ({data}: VDiskPopupProps) => {
257271
<div className={b()}>
258272
{data.DonorMode && <Label className={b('donor-label')}>Donor</Label>}
259273
<InfoViewer title="VDisk" info={vdiskInfo} size="s" />
260-
{pdiskInfo && <InfoViewer title="PDisk" info={pdiskInfo} size="s" />}
274+
{pdiskInfo && isViewerUser && <InfoViewer title="PDisk" info={pdiskInfo} size="s" />}
261275
{donorsInfo.length > 0 && <InfoViewer title="Donors" info={donorsInfo} size="s" />}
262276
</div>
263277
);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"context_not-available": "not available",
3+
"label_state": "State",
4+
"label_storage-pool": "StoragePool",
5+
"label_node-id": "NodeId",
6+
"label_pdisk-id": "PDiskId",
7+
"label_vslot-id": "VSlotId",
8+
"label_links": "Links",
9+
"label_vdisk": "VDisk",
10+
"label_fresh": "Fresh",
11+
"label_level": "Level",
12+
"label_space": "Space",
13+
"label_front-queues": "FrontQueues",
14+
"label_replicated": "Replicated",
15+
"label_progress": "Progress",
16+
"label_remaining": "Remaining",
17+
"label_unsync-vdisks": "UnsyncVDisks",
18+
"label_allocated": "Allocated",
19+
"label_read": "Read",
20+
"label_write": "Write"
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {registerKeysets} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
5+
const COMPONENT = 'ydb-vDisk-popup';
6+
7+
export const vDiskPopupKeyset = registerKeysets(COMPONENT, {en});

src/components/nodesColumns/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export function isMonitoringUserNodesColumn(columnId: string): boolean {
4343
return MONITORING_USER_COLUMNS_IDS.includes(columnId as NodesColumnId);
4444
}
4545

46+
// Columns, that should displayed only for users with isViewerAllowed:true
47+
const VIEWER_USER_COLUMNS_IDS: NodesColumnId[] = ['LoadAverage'];
48+
export function isViewerUserNodesColumn(columnId: string): boolean {
49+
return VIEWER_USER_COLUMNS_IDS.some((el) => el === columnId);
50+
}
51+
4652
// This code is running when module is initialized and correct language may not be set yet
4753
// get functions guarantee that i18n fields will be inited on render with current render language
4854
export const NODES_COLUMNS_TITLES = {

src/containers/Nodes/Nodes.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import React from 'react';
22

33
import type {Column} from '../../components/PaginatedTable';
4-
import {isMonitoringUserNodesColumn} from '../../components/nodesColumns/constants';
4+
import {
5+
isMonitoringUserNodesColumn,
6+
isViewerUserNodesColumn,
7+
} from '../../components/nodesColumns/constants';
58
import type {NodesColumnId} from '../../components/nodesColumns/constants';
69
import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
710
import type {AdditionalNodesProps} from '../../types/additionalProps';
811
import type {NodesGroupByField} from '../../types/api/nodes';
9-
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
12+
import {
13+
useIsUserAllowedToMakeChanges,
14+
useIsViewerUser,
15+
} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
1016

1117
import {PaginatedNodes} from './PaginatedNodes';
1218
import {getNodesColumns} from './columns/columns';
@@ -45,13 +51,20 @@ export function Nodes({
4551
groupByParams = ALL_NODES_GROUP_BY_PARAMS,
4652
}: NodesProps) {
4753
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
54+
const isViewerUser = useIsViewerUser();
4855

4956
const preparedColumns = React.useMemo(() => {
5057
if (isUserAllowedToMakeChanges) {
5158
return columns;
5259
}
53-
return columns.filter((column) => !isMonitoringUserNodesColumn(column.name));
54-
}, [columns, isUserAllowedToMakeChanges]);
60+
const filteredColumns = columns.filter(
61+
(column) => !isMonitoringUserNodesColumn(column.name),
62+
);
63+
if (isViewerUser) {
64+
return filteredColumns;
65+
}
66+
return filteredColumns.filter((column) => !isViewerUserNodesColumn(column.name));
67+
}, [columns, isUserAllowedToMakeChanges, isViewerUser]);
5568

5669
return (
5770
<PaginatedNodes

src/containers/Nodes/NodesControls/NodesControls.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '../../../store/reducers/capabilities/hooks';
1414
import {useProblemFilter} from '../../../store/reducers/settings/hooks';
1515
import type {NodesGroupByField} from '../../../types/api/nodes';
16+
import {useIsViewerUser} from '../../../utils/hooks/useIsUserAllowedToMakeChanges';
1617
import {PeerRoleFilter} from '../PeerRoleFilter/PeerRoleFilter';
1718
import {getNodesGroupByOptions} from '../columns/constants';
1819
import i18n from '../i18n';
@@ -58,12 +59,13 @@ export function NodesControls({
5859
handleGroupByParamChange,
5960
} = useNodesPageQueryParams(groupByParams);
6061
const {problemFilter, handleProblemFilterChange} = useProblemFilter();
62+
const isViewerUser = useIsViewerUser();
6163

6264
const systemStateGroupingAvailable = useViewerNodesHandlerHasGroupingBySystemState();
6365
const groupByoptions = getNodesGroupByOptions(groupByParams, systemStateGroupingAvailable);
6466

6567
const networStatsAvailable = useViewerNodesHandlerHasNetworkStats();
66-
const shouldDisplayPeerRoleFilter = withPeerRoleFilter && networStatsAvailable;
68+
const shouldDisplayPeerRoleFilter = withPeerRoleFilter && networStatsAvailable && isViewerUser;
6769

6870
const handleGroupBySelectUpdate = (value: string[]) => {
6971
handleGroupByParamChange(value[0]);

src/containers/Nodes/useNodesPageQueryParams.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {StringParam, useQueryParams} from 'use-query-params';
22

33
import {useViewerNodesHandlerHasGroupingBySystemState} from '../../store/reducers/capabilities/hooks';
44
import type {NodesGroupByField, NodesPeerRole} from '../../types/api/nodes';
5+
import {useIsViewerUser} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
56
import type {NodesUptimeFilterValues} from '../../utils/nodes';
67
import {nodesUptimeFilterValuesSchema} from '../../utils/nodes';
78

@@ -16,9 +17,13 @@ export function useNodesPageQueryParams(groupByParams: NodesGroupByField[] | und
1617
nodesGroupBy: StringParam,
1718
});
1819

20+
const isViewerUser = useIsViewerUser();
21+
1922
const uptimeFilter = nodesUptimeFilterValuesSchema.parse(queryParams.uptimeFilter);
2023
const searchValue = queryParams.search ?? '';
21-
const peerRoleFilter = parseNodesPeerRoleFilter(queryParams.peerRole);
24+
const peerRoleFilter = isViewerUser
25+
? parseNodesPeerRoleFilter(queryParams.peerRole)
26+
: 'database';
2227

2328
const systemStateGroupingAvailable = useViewerNodesHandlerHasGroupingBySystemState();
2429
const groupByParam = parseNodesGroupByParam(

src/containers/Storage/PaginatedStorageGroupsTable/columns/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export const DEFAULT_STORAGE_GROUPS_COLUMNS: StorageGroupsColumnId[] = [
5555
'VDisks',
5656
];
5757

58+
// Columns, that should displayed only for users with isViewerAllowed:true
59+
const VIEWER_USER_COLUMNS_IDS: StorageGroupsColumnId[] = ['DiskSpace'];
60+
export function isViewerGroupsColumn(columnId: string): boolean {
61+
return VIEWER_USER_COLUMNS_IDS.some((el) => el === columnId);
62+
}
63+
5864
export const REQUIRED_STORAGE_GROUPS_COLUMNS: StorageGroupsColumnId[] = ['GroupId'];
5965

6066
// This code is running when module is initialized and correct language may not be set yet

src/containers/Storage/PaginatedStorageGroupsTable/columns/hooks.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import React from 'react';
22

33
import {VISIBLE_ENTITIES} from '../../../../store/reducers/storage/constants';
4-
import {useIsUserAllowedToMakeChanges} from '../../../../utils/hooks/useIsUserAllowedToMakeChanges';
4+
import {
5+
useIsUserAllowedToMakeChanges,
6+
useIsViewerUser,
7+
} from '../../../../utils/hooks/useIsUserAllowedToMakeChanges';
58
import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns';
69

710
import {getStorageGroupsColumns} from './columns';
@@ -12,6 +15,7 @@ import {
1215
STORAGE_GROUPS_COLUMNS_TITLES,
1316
STORAGE_GROUPS_SELECTED_COLUMNS_LS_KEY,
1417
isMonitoringUserGroupsColumn,
18+
isViewerGroupsColumn,
1519
} from './constants';
1620
import type {GetStorageGroupsColumnsParams} from './types';
1721

@@ -20,15 +24,22 @@ export function useStorageGroupsSelectedColumns({
2024
viewContext,
2125
}: GetStorageGroupsColumnsParams) {
2226
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
27+
const isViewerUser = useIsViewerUser();
2328

2429
const columns = React.useMemo(() => {
2530
const allColumns = getStorageGroupsColumns({viewContext});
2631

2732
if (isUserAllowedToMakeChanges) {
2833
return allColumns;
2934
}
30-
return allColumns.filter((column) => !isMonitoringUserGroupsColumn(column.name));
31-
}, [isUserAllowedToMakeChanges, viewContext]);
35+
const filteredColumns = allColumns.filter(
36+
(column) => !isMonitoringUserGroupsColumn(column.name),
37+
);
38+
if (isViewerUser) {
39+
return filteredColumns;
40+
}
41+
return filteredColumns.filter((column) => !isViewerGroupsColumn(column.name));
42+
}, [isUserAllowedToMakeChanges, viewContext, isViewerUser]);
3243

3344
const requiredColumns = React.useMemo(() => {
3445
if (visibleEntities === VISIBLE_ENTITIES.missing) {

0 commit comments

Comments
 (0)