Skip to content

Commit 73de61a

Browse files
committed
WIP, port RBAC
1 parent 445f4f0 commit 73de61a

8 files changed

Lines changed: 570 additions & 37 deletions

File tree

clients/ui/frontend/src/app/api/service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { APIOptions } from '~/shared/api/types';
2020
import { handleRestFailures } from '~/shared/api/errorUtils';
2121
import { bumpRegisteredModelTimestamp } from '~/app/api/updateTimestamps';
22+
import { ModelRegistryKind } from "~/shared/k8sTypes";
2223

2324
export const createRegisteredModel =
2425
(hostPath: string, queryParams: Record<string, unknown> = {}) =>
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import React from 'react';
2+
import {
3+
Breadcrumb,
4+
BreadcrumbItem,
5+
ClipboardCopy,
6+
Tab,
7+
TabContent,
8+
TabContentBody,
9+
Tabs,
10+
} from '@patternfly/react-core';
11+
import { Link } from 'react-router-dom';
12+
import { Navigate, useParams } from 'react-router';
13+
import { useGroups } from '~/api';
14+
// import { useContextResourceData } from '~/utilities/useContextResourceData';
15+
// import { SupportedArea } from '~/concepts/areas';
16+
import { RoleBindingPermissionsRoleType } from '~/shared/types';
17+
import ApplicationsPage from '~/shared/components/ApplicationsPage';
18+
import { ModelRegistryKind } from '~/shared/k8sTypes';
19+
import RoleBindingPermissions from "~/concepts/roleBinding/roleBindingPermissions";
20+
// import ProjectsSettingsTab from "./ProjectsTab/ProjectSettingsTab";
21+
// import { useModelRegistryNamespaceCR } from '~/concepts/modelRegistry/context/useModelRegistryNamespaceCR';
22+
// import { AreaContext } from '~/concepts/areas/AreaContext';
23+
// import {
24+
// createModelRegistryRoleBinding,
25+
// deleteModelRegistryRoleBinding,
26+
// } from '~/services/modelRegistrySettingsService';
27+
// import RedirectErrorState from '~/pages/external/RedirectErrorState';
28+
// import useModelRegistryRoleBindings from './useModelRegistryRoleBindings';
29+
30+
const ModelRegistriesManagePermissions: React.FC = () => {
31+
// const { dscStatus } = React.useContext(AreaContext);
32+
// const modelRegistryNamespace = dscStatus?.components?.modelregistry?.registriesNamespace;
33+
const [activeTabKey, setActiveTabKey] = React.useState('users');
34+
const [ownerReference, setOwnerReference] = React.useState<ModelRegistryKind>();
35+
const [groups] = useGroups();
36+
// const roleBindings = useContextResourceData<RoleBindingKind>(useModelRegistryRoleBindings());
37+
const { mrName } = useParams<{ mrName: string }>();
38+
// const state = useModelRegistryNamespaceCR(modelRegistryNamespace, mrName || '');
39+
const state = '';
40+
const [modelRegistryCR, crLoaded] = state;
41+
// const filteredRoleBindings = roleBindings.data.filter(
42+
// (rb) => rb.metadata.labels?.['app.kubernetes.io/name'] === mrName,
43+
// );
44+
45+
// const error = !modelRegistryNamespace
46+
// ? new Error('No registries namespace could be found')
47+
// : null;
48+
49+
// React.useEffect(() => {
50+
// if (modelRegistryCR) {
51+
// setOwnerReference(modelRegistryCR);
52+
// } else {
53+
// setOwnerReference(undefined);
54+
// }
55+
// }, [modelRegistryCR]);
56+
57+
// if (!modelRegistryNamespace) {
58+
// return (
59+
// <ApplicationsPage loaded empty={false}>
60+
// <RedirectErrorState title="Could not load component state" errorMessage={error?.message} />
61+
// </ApplicationsPage>
62+
// );
63+
// }
64+
65+
if (
66+
// (roleBindings.loaded && filteredRoleBindings.length === 0) ||
67+
crLoaded &&
68+
!modelRegistryCR
69+
) {
70+
return <Navigate to="/modelRegistrySettings" replace />;
71+
}
72+
73+
return (
74+
<ApplicationsPage
75+
title={`Manage ${mrName ?? ''} permissions`}
76+
description="Manage access to this model registry for individual users and user groups, and for service accounts in a project."
77+
breadcrumb={
78+
<Breadcrumb>
79+
<BreadcrumbItem
80+
render={() => <Link to="/modelRegistrySettings">Model registry settings</Link>}
81+
/>
82+
<BreadcrumbItem isActive>Manage Permissions</BreadcrumbItem>
83+
</Breadcrumb>
84+
}
85+
loaded
86+
empty={false}
87+
provideChildrenPadding
88+
>
89+
<Tabs
90+
activeKey={activeTabKey}
91+
onSelect={(e, tabKey) => {
92+
setActiveTabKey(tabKey.toString());
93+
}}
94+
>
95+
<Tab eventKey="users" title="Users" id="users-tab" tabContentId="users-tab-content" />
96+
<Tab
97+
eventKey="projects"
98+
title="Projects"
99+
id="projects-tab"
100+
data-testid="projects-tab"
101+
tabContentId="projects-tab-content"
102+
/>
103+
</Tabs>
104+
<div>
105+
<TabContent
106+
id="users-tab-content"
107+
eventKey="users"
108+
hidden={activeTabKey !== 'users'}
109+
data-testid="users-tab-content"
110+
>
111+
<TabContentBody>
112+
<RoleBindingPermissions
113+
ownerReference={ownerReference}
114+
defaultRoleBindingName={`${mrName ?? ''}-users`}
115+
isGroupFirst
116+
permissionOptions={[
117+
{
118+
type: RoleBindingPermissionsRoleType.DEFAULT,
119+
description: 'Default role for all users',
120+
},
121+
]}
122+
roleRefKind="Role"
123+
roleRefName={`registry-user-${mrName ?? ''}`}
124+
// labels={{
125+
// [KnownLabels.DASHBOARD_RESOURCE]: 'true',
126+
// app: mrName || '',
127+
// 'app.kubernetes.io/component': SupportedArea.MODEL_REGISTRY,
128+
// 'app.kubernetes.io/part-of': SupportedArea.MODEL_REGISTRY,
129+
// 'app.kubernetes.io/name': mrName || '',
130+
// component: SupportedArea.MODEL_REGISTRY,
131+
// }}
132+
// projectName={modelRegistryNamespace}
133+
description={
134+
<>
135+
To enable access for all cluster users, add{' '}
136+
<ClipboardCopy variant="inline-compact">system:authenticated</ClipboardCopy> to
137+
the group list.
138+
</>
139+
}
140+
// roleBindingPermissionsRB={{ ...roleBindings, data: filteredRoleBindings }}
141+
groups={groups}
142+
createRoleBinding={createModelRegistryRoleBinding}
143+
deleteRoleBinding={deleteModelRegistryRoleBinding}
144+
/>
145+
</TabContentBody>
146+
</TabContent>
147+
<TabContent
148+
id="projects-tab-content"
149+
eventKey="projects"
150+
data-testid="projects-tab-content"
151+
hidden={activeTabKey !== 'projects'}
152+
>
153+
{/* <TabContentBody>
154+
<ProjectsSettingsTab
155+
ownerReference={ownerReference}
156+
permissionOptions={[
157+
{
158+
type: RoleBindingPermissionsRoleType.DEFAULT,
159+
description: 'Default role for all projects',
160+
},
161+
]}
162+
description="To enable access for all service accounts in a project, add the project name to the projects list."
163+
roleRefName={`registry-user-${mrName ?? ''}`}
164+
labels={{
165+
[KnownLabels.DASHBOARD_RESOURCE]: 'true',
166+
[KnownLabels.PROJECT_SUBJECT]: 'true',
167+
app: mrName || '',
168+
'app.kubernetes.io/component': SupportedArea.MODEL_REGISTRY,
169+
'app.kubernetes.io/part-of': SupportedArea.MODEL_REGISTRY,
170+
'app.kubernetes.io/name': mrName || '',
171+
component: SupportedArea.MODEL_REGISTRY,
172+
}}
173+
// projectName={modelRegistryNamespace}
174+
isProjectSubject={activeTabKey === 'projects'}
175+
// roleBindingPermissionsRB={{ ...roleBindings, data: filteredRoleBindings }}
176+
/>
177+
</TabContentBody> */}
178+
</TabContent>
179+
</div>
180+
</ApplicationsPage>
181+
);
182+
};
183+
184+
export default ModelRegistriesManagePermissions;

clients/ui/frontend/src/app/pages/settings/ModelRegistriesTableRow.tsx

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,47 @@ const ModelRegistriesTableRow: React.FC<ModelRegistriesTableRowProps> = ({
4444
]}
4545
/>
4646
</Td>
47-
{isPlatformDefault() && (
48-
<Td modifier="fitContent">
49-
{filteredRoleBindings.length === 0 ? (
50-
<Tooltip content="You can manage permissions when the model registry becomes available.">
51-
<Button isAriaDisabled variant="link">
52-
Manage permissions
53-
</Button>
54-
</Tooltip>
55-
) : (
56-
<Button
57-
variant="link"
58-
onClick={() => navigate(`/model-registry-settings/permissions/${mr.name}`)}
59-
>
47+
{/* {isPlatformDefault() && ( */}
48+
<Td modifier="fitContent">
49+
{/* {filteredRoleBindings.length === 0 ? (
50+
<Tooltip content="You can manage permissions when the model registry becomes available.">
51+
<Button isAriaDisabled variant="link">
6052
Manage permissions
6153
</Button>
62-
)}
63-
</Td>
64-
)}
65-
{isPlatformDefault() && (
66-
<Td isActionCell>
67-
<ActionsColumn
68-
disabled={!isPlatformDefault()}
69-
items={[
70-
{
71-
title: 'Edit model registry',
72-
disabled: !isPlatformDefault(),
73-
onClick: () => {
74-
onEditRegistry(mr);
75-
},
54+
</Tooltip>
55+
) : ( */}
56+
<Button
57+
variant="link"
58+
onClick={() => navigate(`/model-registry-settings/permissions/${mr.name}`)}
59+
>
60+
Manage permissions
61+
</Button>
62+
{/* )} */}
63+
</Td>
64+
{/* )} */}
65+
{/* {isPlatformDefault() && ( */}
66+
<Td isActionCell>
67+
<ActionsColumn
68+
disabled={!isPlatformDefault()}
69+
items={[
70+
{
71+
title: 'Edit model registry',
72+
disabled: !isPlatformDefault(),
73+
onClick: () => {
74+
onEditRegistry(mr);
7675
},
77-
{
78-
title: 'Delete model registry',
79-
disabled: !isPlatformDefault(),
80-
onClick: () => {
81-
onDeleteRegistry(mr);
82-
},
76+
},
77+
{
78+
title: 'Delete model registry',
79+
disabled: !isPlatformDefault(),
80+
onClick: () => {
81+
onDeleteRegistry(mr);
8382
},
84-
]}
85-
/>
86-
</Td>
87-
)}
83+
},
84+
]}
85+
/>
86+
</Td>
87+
{/* )} */}
8888
</Tr>
8989
);
9090
};

clients/ui/frontend/src/app/pages/settings/ModelRegistrySettingsRoutes.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import * as React from 'react';
22
import { Navigate, Routes, Route } from 'react-router-dom';
33
import ModelRegistrySettings from './ModelRegistrySettings';
4+
import ModelRegistriesPermissions from './ModelRegistriesPermissions';
45

56
const ModelRegistrySettingsRoutes: React.FC = () => (
67
<Routes>
78
<Route path="/" element={<ModelRegistrySettings />} />
89
<Route path="*" element={<Navigate to="/" />} />
10+
<Route path="/permissions" element={<ModelRegistriesPermissions />} />
911
</Routes>
1012
);
1113

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { capitalize } from '@patternfly/react-core';
2+
import { ProjectKind, RoleBindingKind } from '~/k8sTypes';
3+
import { namespaceToProjectDisplayName } from '~/concepts/projects/utils';
4+
import { RoleBindingPermissionsRBType, RoleBindingPermissionsRoleType } from './types';
5+
6+
export const filterRoleBindingSubjects = (
7+
roleBindings: RoleBindingKind[],
8+
type: RoleBindingPermissionsRBType,
9+
isProjectSubject?: boolean,
10+
): RoleBindingKind[] =>
11+
roleBindings.filter(
12+
(roles) =>
13+
roles.subjects[0]?.kind === type &&
14+
(isProjectSubject
15+
? roles.metadata.labels?.['opendatahub.io/rb-project-subject'] === 'true'
16+
: !(roles.metadata.labels?.['opendatahub.io/rb-project-subject'] === 'true')),
17+
);
18+
19+
export const castRoleBindingPermissionsRoleType = (
20+
role: string,
21+
): RoleBindingPermissionsRoleType => {
22+
if (role === RoleBindingPermissionsRoleType.ADMIN) {
23+
return RoleBindingPermissionsRoleType.ADMIN;
24+
}
25+
if (role === RoleBindingPermissionsRoleType.EDIT) {
26+
return RoleBindingPermissionsRoleType.EDIT;
27+
}
28+
return RoleBindingPermissionsRoleType.DEFAULT;
29+
};
30+
31+
export const firstSubject = (
32+
roleBinding: RoleBindingKind,
33+
isProjectSubject?: boolean,
34+
project?: ProjectKind[],
35+
): string =>
36+
(isProjectSubject && project
37+
? namespaceToProjectDisplayName(
38+
roleBinding.subjects[0]?.name.replace(/^system:serviceaccounts:/, ''),
39+
project,
40+
)
41+
: roleBinding.subjects[0]?.name) || '';
42+
43+
export const roleLabel = (value: RoleBindingPermissionsRoleType): string => {
44+
if (value === RoleBindingPermissionsRoleType.EDIT) {
45+
return 'Contributor';
46+
}
47+
return capitalize(value);
48+
};
49+
50+
export const removePrefix = (roleBindings: RoleBindingKind[]): string[] =>
51+
roleBindings.map((rb) => rb.subjects[0]?.name.replace(/^system:serviceaccounts:/, ''));

0 commit comments

Comments
 (0)