|
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