From fbc07ee2f2da6696d025a3c4fe1b51cd1c849661 Mon Sep 17 00:00:00 2001 From: Martin Hradil Date: Wed, 20 Sep 2023 20:50:38 +0000 Subject: [PATCH 1/3] Access tab - add users Issue: AAH-2284 --- CHANGES/2284.feature | 1 + src/api/response-types/namespace.ts | 1 + src/components/index.ts | 3 +- src/components/rbac/access-tab.tsx | 446 +++++++++++++++--- src/components/rbac/preview-roles.tsx | 29 +- ...e-permissions.tsx => role-permissions.tsx} | 2 +- src/components/rbac/select-group.tsx | 2 +- src/components/rbac/select-user.tsx | 212 +++++++++ src/containers/ansible-remote/detail.tsx | 11 +- src/containers/ansible-remote/tab-access.tsx | 143 +++++- src/containers/ansible-repository/detail.tsx | 17 +- .../ansible-repository/tab-access.tsx | 143 +++++- .../execution_environment_detail_access.tsx | 235 ++++++--- .../namespace-detail/namespace-detail.tsx | 229 ++++++--- src/utilities/assign-roles.ts | 34 ++ src/utilities/index.ts | 1 + 16 files changed, 1231 insertions(+), 278 deletions(-) create mode 100644 CHANGES/2284.feature rename src/components/rbac/{group-role-permissions.tsx => role-permissions.tsx} (92%) create mode 100644 src/components/rbac/select-user.tsx create mode 100644 src/utilities/assign-roles.ts diff --git a/CHANGES/2284.feature b/CHANGES/2284.feature new file mode 100644 index 0000000000..8a6c57d0f7 --- /dev/null +++ b/CHANGES/2284.feature @@ -0,0 +1 @@ +Add Users section to Access tab (Namespaces, Remotes, Repositories, EEs) diff --git a/src/api/response-types/namespace.ts b/src/api/response-types/namespace.ts index f05ebd7845..46d421ff8c 100644 --- a/src/api/response-types/namespace.ts +++ b/src/api/response-types/namespace.ts @@ -17,6 +17,7 @@ export class NamespaceListType { export class NamespaceType extends NamespaceListType { groups: GroupObjectPermissionType[]; + users: { username: string; object_roles: string[] }[]; resources: string; owners: string[]; links: NamespaceLinkType[]; diff --git a/src/components/index.ts b/src/components/index.ts index af6a56cffe..82f0b62dbe 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -87,12 +87,12 @@ export { AccessTab } from './rbac/access-tab'; export { DeleteGroupModal } from './rbac/delete-group-modal'; export { DeleteUserModal } from './rbac/delete-user-modal'; export { GroupModal } from './rbac/group-modal'; -export { GroupRolePermissions } from './rbac/group-role-permissions'; export { PermissionCategories } from './rbac/permission-categories'; export { PermissionChipSelector } from './rbac/permission-chip-selector'; export { PreviewRoles } from './rbac/preview-roles'; export { RoleForm } from './rbac/role-form'; export { RoleHeader } from './rbac/role-header'; +export { RolePermissions } from './rbac/role-permissions'; export { CheckboxRow, ExpandableRow, @@ -101,6 +101,7 @@ export { } from './rbac/role-list-table'; export { SelectGroup } from './rbac/select-group'; export { SelectRoles } from './rbac/select-roles'; +export { SelectUser } from './rbac/select-user'; export { UserForm } from './rbac/user-form'; export { UserFormPage } from './rbac/user-form-page'; export { RenderPluginDoc } from './render-plugin-doc/render-plugin-doc'; diff --git a/src/components/rbac/access-tab.tsx b/src/components/rbac/access-tab.tsx index a5da3b5f33..612f6244b4 100644 --- a/src/components/rbac/access-tab.tsx +++ b/src/components/rbac/access-tab.tsx @@ -2,6 +2,7 @@ import { Trans, t } from '@lingui/macro'; import { Button, DropdownItem, + Spinner, Toolbar, ToolbarContent, ToolbarItem, @@ -13,61 +14,110 @@ import { GroupType, RoleType } from 'src/api'; import { DeleteModal, EmptyStateNoData, + EmptyStateXs, ExpandableRow, - GroupRolePermissions, ListItemActions, LoadingPageSpinner, PreviewRoles, RoleListTable, + RolePermissions, SelectGroup, SelectRoles, + SelectUser, SortTable, WizardModal, } from 'src/components'; import { ParamHelper } from 'src/utilities'; +interface UserType { + username: string; + object_roles: string[]; +} + interface IProps { + // users + user?: UserType; + users: UserType[]; + addUser?: (user, roles) => void; + removeUser?: (user) => void; + addUserRole?: (role, users) => void; + removeUserRole?: (role, user) => void; + showUserRemoveModal?: UserType; + showUserSelectWizard?: { user?: UserType; roles?: RoleType[] }; + + // groups group?: GroupType; groups: GroupType[]; - name: string; - pulpObjectType: string; - selectRolesMessage: string; - urlPrefix: string; - canEditOwners: boolean; addGroup?: (group, roles) => void; removeGroup?: (group) => void; addRole?: (role, groups) => void; removeRole?: (role, group) => void; showGroupRemoveModal?: GroupType; showGroupSelectWizard?: { group?: GroupType; roles?: RoleType[] }; + + // roles showRoleRemoveModal?: string; showRoleSelectWizard?: { roles?: RoleType[] }; + + // parent + name: string; + pulpObjectType: string; + selectRolesMessage: string; + urlPrefix: string; + canEditOwners: boolean; updateProps: (prop) => void; } +const SectionTitle = ({ title }: { title: string }) => ( +

{title}

+); +const SectionSeparator = () => ( +
+); + export class AccessTab extends React.Component { render() { - const { groups, group, canEditOwners } = this.props; - const { showGroupRemoveModal, showGroupSelectWizard } = this.props; - const loading = !groups; - const noData = groups?.length === 0; + const { + canEditOwners, + group, + groups, + showGroupRemoveModal, + showGroupSelectWizard, + showUserRemoveModal, + showUserSelectWizard, + user, + users, + updateProps, + } = this.props; - const buttonAdd = ( - ); + const buttonUserAdd = buttonAdd(t`Select a user`, { + showUserSelectWizard: {}, + }); + const buttonGroupAdd = buttonAdd(t`Select a group`, { + showGroupSelectWizard: {}, + }); + return loading ? ( ) : ( <> + {showUserRemoveModal ? this.renderUserRemoveModal() : null} + {showUserSelectWizard ? this.renderUserSelectWizard() : null} {showGroupRemoveModal ? this.renderGroupRemoveModal() : null} {showGroupSelectWizard ? this.renderGroupSelectWizard() : null} @@ -76,23 +126,111 @@ export class AccessTab extends React.Component { title={t`There are currently no owners assigned.`} description={ canEditOwners - ? t`Please add an owner by using the button below.` + ? t`Please add an owner by using the buttons below.` : '' } + button={ + canEditOwners ? ( + <> + {buttonUserAdd} {buttonGroupAdd} + + ) : null + } + /> + ) : user || group ? ( + this.renderRoles() + ) : ( + <> + {this.renderSection({ + buttonAdd: buttonUserAdd, + canEditOwners, + emptyStateTitle: t`There are currently no users assigned.`, + emptyStateExtra: t`Except for members of groups below.`, + items: users, + renderItems: () => + this.renderList({ + ariaLabel: t`User list`, + canEditOwners, + itemName: t`User`, + buttonAdd: buttonUserAdd, + items: users, + renderItem: (item, index) => this.renderUserRow(item, index), + sortField: 'username', + }), + title: t`Users`, + })} + + {this.renderSection({ + buttonAdd: buttonGroupAdd, + canEditOwners, + emptyStateTitle: t`There are currently no groups assigned.`, + items: groups, + renderItems: () => + this.renderList({ + ariaLabel: t`Group list`, + canEditOwners, + itemName: t`Group`, + buttonAdd: buttonGroupAdd, + items: groups, + renderItem: (item, index) => this.renderGroupRow(item, index), + sortField: 'name', + }), + title: t`Groups`, + })} + + )} + + ); + } + + private renderSection({ + buttonAdd, + canEditOwners, + emptyStateTitle, + emptyStateExtra = '', + items, + renderItems, + title, + }) { + const loading = !items; + const noData = items?.length === 0; + + return ( + <> + + {loading ? ( + + ) : noData ? ( + + {emptyStateExtra} + {emptyStateExtra &&
} + {canEditOwners + ? t`Please add an owner by using the button below.` + : ''} + + } button={canEditOwners ? buttonAdd : null} /> - ) : group ? ( - this.renderRoles({ group }) ) : ( - this.renderGroups({ buttonAdd, groups }) + renderItems() )} ); } - private renderGroups({ buttonAdd, groups }) { - const { canEditOwners } = this.props; - const sortedGroups = sortBy(groups, 'name'); + private renderList({ + ariaLabel, + buttonAdd, + canEditOwners, + itemName, + items, + renderItem, + sortField, + }) { + const sorted = sortBy(items, sortField); return ( <> @@ -107,16 +245,16 @@ export class AccessTab extends React.Component { )} { params={{}} updateParams={() => null} /> - - {sortedGroups.map((group, i) => this.renderGroupRow(group, i))} - + {sorted.map(renderItem)}
); } + private renderUserRow(user, index: number) { + const { urlPrefix, canEditOwners, updateProps } = this.props; + + const dropdownItems = [ + canEditOwners && ( + + updateProps({ + showUserRemoveModal: user, + }) + } + > + Remove user + + ), + ]; + + return ( + + + + {user.username} + + + + + ); + } + private renderGroupRow(group, index: number) { - const { urlPrefix, canEditOwners } = this.props; + const { urlPrefix, canEditOwners, updateProps } = this.props; const dropdownItems = [ canEditOwners && ( { - this.props.updateProps({ + onClick={() => + updateProps({ showGroupRemoveModal: group, - }); - }} + }) + } > Remove group @@ -155,14 +330,14 @@ export class AccessTab extends React.Component { ]; return ( - + { ); } - private renderRoles({ group }) { - const { canEditOwners } = this.props; - const { showRoleRemoveModal, showRoleSelectWizard } = this.props; - const roles = group?.object_roles; - const sortedRoles = sortBy(roles); + private renderRoles() { + const { + canEditOwners, + group, + showRoleRemoveModal, + showRoleSelectWizard, + updateProps, + user, + } = this.props; - if (!group) { + if ((!user && !group) || (user && group)) { return null; } + const roles = (user || group).object_roles; + const sortedRoles = sortBy(roles); + const buttonAdd = (