|
1 | | -import React from 'react'; |
| 1 | +import {Button, Flex, Icon, Text} from '@gravity-ui/uikit'; |
2 | 2 |
|
3 | | -import {DefinitionList} from '@gravity-ui/components'; |
4 | | -import type {DefinitionListItem} from '@gravity-ui/components'; |
5 | | -import {SquareCheck} from '@gravity-ui/icons'; |
6 | | -import {Icon} from '@gravity-ui/uikit'; |
7 | | - |
8 | | -import {ResponseError} from '../../../components/Errors/ResponseError'; |
9 | | -import {Loader} from '../../../components/Loader'; |
10 | | -import {schemaAclApi} from '../../../store/reducers/schemaAcl/schemaAcl'; |
11 | | -import type {TACE} from '../../../types/api/acl'; |
12 | | -import {valueIsDefined} from '../../../utils'; |
13 | | -import {cn} from '../../../utils/cn'; |
| 3 | +import { |
| 4 | + TENANT_DIAGNOSTICS_TABS_IDS, |
| 5 | + TENANT_PAGES_IDS, |
| 6 | +} from '../../../store/reducers/tenant/constants'; |
| 7 | +import {setDiagnosticsTab, setTenantPage} from '../../../store/reducers/tenant/tenant'; |
| 8 | +import {useTypedDispatch} from '../../../utils/hooks'; |
14 | 9 |
|
15 | 10 | import i18n from './i18n'; |
16 | 11 |
|
17 | | -import './Acl.scss'; |
18 | | - |
19 | | -const b = cn('ydb-acl'); |
20 | | - |
21 | | -const prepareLogin = (value: string | undefined) => { |
22 | | - if (value && value.endsWith('@staff') && !value.startsWith('svc_')) { |
23 | | - const login = value.split('@')[0]; |
24 | | - return login; |
25 | | - } |
26 | | - |
27 | | - return value; |
28 | | -}; |
29 | | - |
30 | | -const aclParams = ['access', 'type', 'inheritance'] as const; |
31 | | - |
32 | | -type AclParameter = (typeof aclParams)[number]; |
33 | | - |
34 | | -const aclParamToName: Record<AclParameter, string> = { |
35 | | - access: 'Access', |
36 | | - type: 'Access type', |
37 | | - inheritance: 'Inheritance type', |
38 | | -}; |
39 | | - |
40 | | -const defaultInheritanceType = ['Object', 'Container']; |
41 | | -const defaultAccessType = 'Allow'; |
42 | | - |
43 | | -const defaultInheritanceTypeSet = new Set(defaultInheritanceType); |
44 | | - |
45 | | -function normalizeAcl(acl: TACE[]) { |
46 | | - return acl.map((ace) => { |
47 | | - const {AccessRules = [], AccessRights = [], AccessType, InheritanceType, Subject} = ace; |
48 | | - const access = AccessRules.concat(AccessRights); |
49 | | - //"Allow" is default access type. We want to show it only if it isn't default |
50 | | - const type = AccessType === defaultAccessType ? undefined : AccessType; |
51 | | - let inheritance; |
52 | | - // ['Object', 'Container'] - is default inheritance type. We want to show it only if it isn't default |
53 | | - if ( |
54 | | - InheritanceType?.length !== defaultInheritanceTypeSet.size || |
55 | | - InheritanceType.some((t) => !defaultInheritanceTypeSet.has(t)) |
56 | | - ) { |
57 | | - inheritance = InheritanceType; |
58 | | - } |
59 | | - return { |
60 | | - access: access.length ? access : undefined, |
61 | | - type, |
62 | | - inheritance, |
63 | | - Subject, |
64 | | - }; |
65 | | - }); |
66 | | -} |
| 12 | +import ArrowRightFromSquareIcon from '@gravity-ui/icons/svgs/arrow-right-from-square.svg'; |
67 | 13 |
|
68 | | -interface DefinitionValueProps { |
69 | | - value: string | string[]; |
70 | | -} |
| 14 | +export const Acl = () => { |
| 15 | + const dispatch = useTypedDispatch(); |
71 | 16 |
|
72 | | -function DefinitionValue({value}: DefinitionValueProps) { |
73 | | - const normalizedValue = typeof value === 'string' ? [value] : value; |
74 | 17 | return ( |
75 | | - <div className={b('definition-content')}> |
76 | | - {normalizedValue.map((el) => ( |
77 | | - <span key={el}>{el}</span> |
78 | | - ))} |
79 | | - </div> |
80 | | - ); |
81 | | -} |
82 | | - |
83 | | -function getAclListItems(acl?: TACE[]): DefinitionListItem[] { |
84 | | - if (!acl || !acl.length) { |
85 | | - return []; |
86 | | - } |
87 | | - |
88 | | - const normalizedAcl = normalizeAcl(acl); |
89 | | - |
90 | | - return normalizedAcl.map(({Subject, ...data}) => { |
91 | | - const definedDataEntries = Object.entries(data).filter(([_key, value]) => |
92 | | - Boolean(value), |
93 | | - ) as [AclParameter, string | string[]][]; |
94 | | - |
95 | | - if (definedDataEntries.length === 1 && definedDataEntries[0][0] === 'access') { |
96 | | - return { |
97 | | - name: Subject, |
98 | | - content: <DefinitionValue value={definedDataEntries[0][1]} />, |
99 | | - multilineName: true, |
100 | | - }; |
101 | | - } |
102 | | - return { |
103 | | - label: <span className={b('group-label')}>{Subject}</span>, |
104 | | - items: aclParams |
105 | | - .map((key) => { |
106 | | - const value = data[key]; |
107 | | - if (value) { |
108 | | - return { |
109 | | - name: aclParamToName[key], |
110 | | - content: <DefinitionValue value={value} />, |
111 | | - multilineName: true, |
112 | | - }; |
113 | | - } |
114 | | - return undefined; |
115 | | - }) |
116 | | - .filter(valueIsDefined), |
117 | | - }; |
118 | | - }); |
119 | | -} |
120 | | - |
121 | | -function getOwnerItem(owner?: string): DefinitionListItem[] { |
122 | | - const preparedOwner = prepareLogin(owner); |
123 | | - if (!preparedOwner) { |
124 | | - return []; |
125 | | - } |
126 | | - return [ |
127 | | - { |
128 | | - name: preparedOwner, |
129 | | - content: i18n('title_owner'), |
130 | | - multilineName: true, |
131 | | - }, |
132 | | - ]; |
133 | | -} |
134 | | - |
135 | | -function getInterruptInheritanceItem(flag?: boolean): DefinitionListItem[] { |
136 | | - if (!flag) { |
137 | | - return []; |
138 | | - } |
139 | | - return [ |
140 | | - { |
141 | | - name: i18n('title_interupt-inheritance'), |
142 | | - content: <Icon data={SquareCheck} size={20} />, |
143 | | - multilineName: true, |
144 | | - }, |
145 | | - ]; |
146 | | -} |
147 | | - |
148 | | -export const Acl = ({path, database}: {path: string; database: string}) => { |
149 | | - const {currentData, isFetching, error} = schemaAclApi.useGetSchemaAclQuery({path, database}); |
150 | | - |
151 | | - const loading = isFetching && !currentData; |
152 | | - |
153 | | - const {acl, effectiveAcl, owner, interruptInheritance} = currentData || {}; |
154 | | - |
155 | | - const aclListItems = getAclListItems(acl); |
156 | | - const effectiveAclListItems = getAclListItems(effectiveAcl); |
157 | | - |
158 | | - const ownerItem = getOwnerItem(owner); |
159 | | - |
160 | | - const interruptInheritanceItem = getInterruptInheritanceItem(interruptInheritance); |
161 | | - |
162 | | - if (loading) { |
163 | | - return <Loader />; |
164 | | - } |
165 | | - |
166 | | - if (error) { |
167 | | - return <ResponseError error={error} />; |
168 | | - } |
169 | | - |
170 | | - if (!acl && !owner && !effectiveAcl) { |
171 | | - return <React.Fragment>{i18n('description_empty')}</React.Fragment>; |
172 | | - } |
173 | | - |
174 | | - const accessRightsItems = ownerItem.concat(aclListItems); |
175 | | - |
176 | | - return ( |
177 | | - <div className={b()}> |
178 | | - <AclDefinitionList items={interruptInheritanceItem} /> |
179 | | - <AclDefinitionList items={accessRightsItems} title={i18n('title_rights')} /> |
180 | | - <AclDefinitionList |
181 | | - items={effectiveAclListItems} |
182 | | - title={i18n('title_effective-rights')} |
183 | | - /> |
184 | | - </div> |
| 18 | + <Flex gap={2} alignItems="center"> |
| 19 | + <Text variant="body-2">{i18n('description_section-moved')}</Text> |
| 20 | + <Button |
| 21 | + title={i18n('action-open-in-diagnostics')} |
| 22 | + onClick={() => { |
| 23 | + dispatch(setTenantPage(TENANT_PAGES_IDS.diagnostics)); |
| 24 | + dispatch(setDiagnosticsTab(TENANT_DIAGNOSTICS_TABS_IDS.access)); |
| 25 | + }} |
| 26 | + size="s" |
| 27 | + > |
| 28 | + <Icon data={ArrowRightFromSquareIcon} size={14} /> |
| 29 | + </Button> |
| 30 | + </Flex> |
185 | 31 | ); |
186 | 32 | }; |
187 | | - |
188 | | -interface AclDefinitionListProps { |
189 | | - items: DefinitionListItem[]; |
190 | | - title?: string; |
191 | | -} |
192 | | - |
193 | | -function AclDefinitionList({items, title}: AclDefinitionListProps) { |
194 | | - if (!items.length) { |
195 | | - return null; |
196 | | - } |
197 | | - return ( |
198 | | - <React.Fragment> |
199 | | - {title && <div className={b('list-title')}>{title}</div>} |
200 | | - <DefinitionList |
201 | | - items={items} |
202 | | - nameMaxWidth={200} |
203 | | - className={b('result', {'no-title': !title})} |
204 | | - responsive |
205 | | - /> |
206 | | - </React.Fragment> |
207 | | - ); |
208 | | -} |
0 commit comments