diff --git a/web/packages/teleport/src/Roles/RoleList/RoleList.tsx b/web/packages/teleport/src/Roles/RoleList/RoleList.tsx index baf15b596c534..3fec1ddd63867 100644 --- a/web/packages/teleport/src/Roles/RoleList/RoleList.tsx +++ b/web/packages/teleport/src/Roles/RoleList/RoleList.tsx @@ -39,6 +39,7 @@ export function RoleList({ serversidePagination: SeversidePagination; rolesAcl: Access; }) { + const canView = rolesAcl.list && rolesAcl.read; const canEdit = rolesAcl.edit; const canDelete = rolesAcl.remove; @@ -72,6 +73,7 @@ export function RoleList({ altKey: 'options-btn', render: (role: RoleResource) => ( onEdit(role.id)} @@ -87,18 +89,23 @@ export function RoleList({ } const ActionCell = (props: { + canView: boolean; canEdit: boolean; canDelete: boolean; onEdit(): void; onDelete(): void; }) => { - if (!(props.canEdit || props.canDelete)) { + if (!(props.canView || props.canDelete)) { return ; } return ( - {props.canEdit && Edit} + {props.canView && ( + + {props.canEdit ? 'Edit' : 'View Details'} + + )} {props.canDelete && ( Delete )} diff --git a/web/packages/teleport/src/Roles/Roles.test.tsx b/web/packages/teleport/src/Roles/Roles.test.tsx index 1a4338cac036e..071c1e10eea3d 100644 --- a/web/packages/teleport/src/Roles/Roles.test.tsx +++ b/web/packages/teleport/src/Roles/Roles.test.tsx @@ -120,13 +120,13 @@ describe('Roles list', () => { expect(menuItems).toHaveLength(2); }); - test('hides edit button if no access', async () => { + test('hides view/edit button if no access', async () => { const ctx = createTeleportContext(); const testState = { ...defaultState, rolesAcl: { ...defaultState.rolesAcl, - edit: false, + list: false, }, }; @@ -147,12 +147,15 @@ describe('Roles list', () => { fireEvent.click(optionsButton); const menuItems = screen.queryAllByRole('menuitem'); expect(menuItems).toHaveLength(1); - expect(menuItems.every(item => item.textContent.includes('Edit'))).not.toBe( - true - ); + expect( + menuItems.every( + item => + item.textContent.includes('View') || item.textContent.includes('Edit') + ) + ).not.toBe(true); }); - test('hides delete button if no access', async () => { + test('hides delete button if user does not have permission to delete', async () => { const ctx = createTeleportContext(); const testState = { ...defaultState, @@ -184,12 +187,14 @@ describe('Roles list', () => { ).not.toBe(true); }); - test('hides Options button if no permissions to edit or delete', async () => { + test('displays Options button if user has permission to list/read roles', async () => { const ctx = createTeleportContext(); const testState = { ...defaultState, rolesAcl: { - ...defaultState.rolesAcl, + list: true, + read: true, + create: false, remove: false, edit: false, }, @@ -203,6 +208,35 @@ describe('Roles list', () => { ); + await waitFor(() => { + expect(screen.getByText('cool-role')).toBeInTheDocument(); + }); + const optionsButton = screen.getByRole('button', { name: /options/i }); + fireEvent.click(optionsButton); + const menuItems = screen.queryAllByRole('menuitem'); + expect(menuItems).toHaveLength(1); + expect(menuItems[0]).toHaveTextContent('View'); + }); + + test('hides Options button if no permissions to view or delete', async () => { + const ctx = createTeleportContext(); + const testState = { + ...defaultState, + rolesAcl: { + ...defaultState.rolesAcl, + remove: false, + list: false, + }, + }; + + render( + + + + + + ); + await waitFor(() => { expect(screen.getByText('cool-role')).toBeInTheDocument(); });