diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistrySettings.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistrySettings.cy.ts index e28c313a8b..c53db78230 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistrySettings.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistrySettings.cy.ts @@ -12,6 +12,16 @@ import { asProjectAdminUser, } from '~/__tests__/cypress/cypress/utils/mockUsers'; import { mockModelRegistry } from '~/__mocks__/mockModelRegistry'; +import type { RoleBindingSubject } from '~/k8sTypes'; +import { mockRoleBindingK8sResource } from '~/__mocks__/mockRoleBindingK8sResource'; + +const groupSubjects: RoleBindingSubject[] = [ + { + kind: 'Group', + apiGroup: 'rbac.authorization.k8s.io', + name: 'example-mr-users', + }, +]; const setupMocksForMRSettingAccess = ({ hasModelRegistries = true, @@ -71,6 +81,19 @@ const setupMocksForMRSettingAccess = ({ req.reply(500); // Something went wrong on the backend when decoding the secret }, ); + + cy.interceptOdh( + 'GET /api/modelRegistryRoleBindings', + mockK8sResourceList([ + mockRoleBindingK8sResource({ + namespace: 'odh-model-registries', + name: 'example-mr-user', + subjects: groupSubjects, + roleRefName: 'registry-user-example-mr', + modelRegistryName: 'example-mr', + }), + ]), + ); }; it('Model registry settings should not be available for non product admins', () => { diff --git a/frontend/src/pages/modelRegistrySettings/ModelRegistriesTable.tsx b/frontend/src/pages/modelRegistrySettings/ModelRegistriesTable.tsx index 011e7a67f4..3afdb311d7 100644 --- a/frontend/src/pages/modelRegistrySettings/ModelRegistriesTable.tsx +++ b/frontend/src/pages/modelRegistrySettings/ModelRegistriesTable.tsx @@ -1,18 +1,21 @@ import React from 'react'; import { Button, Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core'; import { Table } from '~/components/table'; -import { ModelRegistryKind } from '~/k8sTypes'; +import { ModelRegistryKind, RoleBindingKind } from '~/k8sTypes'; +import { ContextResourceData } from '~/types'; import { modelRegistryColumns } from './columns'; import ModelRegistriesTableRow from './ModelRegistriesTableRow'; type ModelRegistriesTableProps = { modelRegistries: ModelRegistryKind[]; refresh: () => Promise; + roleBindings: ContextResourceData; onCreateModelRegistryClick: () => void; }; const ModelRegistriesTable: React.FC = ({ modelRegistries, + roleBindings, refresh, onCreateModelRegistryClick, }) => ( @@ -36,7 +39,12 @@ const ModelRegistriesTable: React.FC = ({ } rowRenderer={(mr) => ( - + )} variant="compact" /> diff --git a/frontend/src/pages/modelRegistrySettings/ModelRegistriesTableRow.tsx b/frontend/src/pages/modelRegistrySettings/ModelRegistriesTableRow.tsx index cd75a344f5..88376c2f8c 100644 --- a/frontend/src/pages/modelRegistrySettings/ModelRegistriesTableRow.tsx +++ b/frontend/src/pages/modelRegistrySettings/ModelRegistriesTableRow.tsx @@ -1,23 +1,33 @@ import React from 'react'; import { ActionsColumn, Td, Tr } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; -import { ModelRegistryKind } from '~/k8sTypes'; +import { Tooltip } from '@patternfly/react-core'; +import { ModelRegistryKind, RoleBindingKind } from '~/k8sTypes'; import ResourceNameTooltip from '~/components/ResourceNameTooltip'; +import { ContextResourceData } from '~/types'; import ViewDatabaseConfigModal from './ViewDatabaseConfigModal'; import DeleteModelRegistryModal from './DeleteModelRegistryModal'; import { ModelRegistryTableRowStatus } from './ModelRegistryTableRowStatus'; type ModelRegistriesTableRowProps = { modelRegistry: ModelRegistryKind; + roleBindings: ContextResourceData; refresh: () => Promise; }; const ModelRegistriesTableRow: React.FC = ({ modelRegistry: mr, + roleBindings, refresh, }) => { const [isDatabaseConfigModalOpen, setIsDatabaseConfigModalOpen] = React.useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false); + const filteredRoleBindings = roleBindings.data.filter( + (rb) => + rb.metadata.labels?.['app.kubernetes.io/name'] === + (mr.metadata.name || mr.metadata.annotations?.['openshift.io/display-name']), + ); + return ( <> @@ -35,12 +45,18 @@ const ModelRegistriesTableRow: React.FC = ({ - - Manage permissions - + {filteredRoleBindings.length === 0 ? ( + + Manage permissions + + ) : ( + + Manage permissions + + )} { const [createModalOpen, setCreateModalOpen] = React.useState(false); - const [modelRegistries, loaded, loadError, refreshModelRegistries] = useModelRegistriesBackend(); + const [modelRegistries, mrloaded, loadError, refreshModelRegistries] = + useModelRegistriesBackend(); + const roleBindings = useContextResourceData(useModelRegistryRoleBindings()); const { refreshRulesReview } = React.useContext(ModelRegistrySelectorContext); + const loaded = mrloaded && roleBindings.loaded; const refreshAll = React.useCallback( () => Promise.all([refreshModelRegistries(), refreshRulesReview()]), @@ -65,6 +71,7 @@ const ModelRegistrySettings: React.FC = () => { > { setCreateModalOpen(true);