From 0602a5ceb1b8cd4bf23af63d3b3d1779c44f9af9 Mon Sep 17 00:00:00 2001 From: Carla Martinez Date: Fri, 16 Feb 2024 17:20:33 +0100 Subject: [PATCH] Prepare endpoint, hook, and data Previous preparation of some elements is needed: - The `User` data type needs the following parameters that will be managed in the 'Is a member of' section: - `memberof_group` - `memberof_netgroup` - `memberof_role` - `memberof_hbacrule` - `memberof_sudorule` - `memberof_subid` - The `useGettingGroupsQuery` endpoint wrapper to make the API call - The `UserGroupNew` data type for replacing the deprecated `UserGroup` one - The `useUserMemberOfData` hook to retrieve and parse the data - The `normalizeString` helper function to normalize string LDAP values Signed-off-by: Carla Martinez --- src/hooks/useUserMemberOfData.tsx | 67 ++++++++++++++++++++++++++ src/services/rpc.ts | 22 ++++++++- src/utils/datatypes/globalDataTypes.ts | 13 ++++- src/utils/userUtils.tsx | 4 ++ src/utils/utils.tsx | 19 ++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/hooks/useUserMemberOfData.tsx diff --git a/src/hooks/useUserMemberOfData.tsx b/src/hooks/useUserMemberOfData.tsx new file mode 100644 index 000000000..cc5a6c9f2 --- /dev/null +++ b/src/hooks/useUserMemberOfData.tsx @@ -0,0 +1,67 @@ +// RPC +import React from "react"; +import { BatchRPCResponse, useGettingGroupsQuery } from "src/services/rpc"; +// Data types +import { UserGroupNew } from "src/utils/datatypes/globalDataTypes"; +// Utils +import { API_VERSION_BACKUP, normalizeString } from "src/utils/utils"; + +type MemberOfData = { + isLoading: boolean; + isFetching: boolean; + refetch: () => void; + userGroupsFullList: UserGroupNew[]; +}; + +const useUserMemberOfData = ({ firstUserIdx, lastUserIdx }): MemberOfData => { + // [API call] User groups + // TODO: Normalize data to prevent array of arrays + const userGroupsQuery = useGettingGroupsQuery({ + searchValue: "", + sizeLimit: 0, + apiVersion: API_VERSION_BACKUP, + startIdx: firstUserIdx, + stopIdx: lastUserIdx, + }); + + const [userGroupsFullList, setUserGroupsFullList] = React.useState< + UserGroupNew[] + >([]); + const userGroupsData = userGroupsQuery.data || {}; + const isUserGroupsLoading = userGroupsQuery.isLoading; + + React.useEffect(() => { + if (userGroupsData !== undefined && !userGroupsQuery.isFetching) { + const dataParsed = userGroupsData as BatchRPCResponse; + const count = dataParsed.result.count; + const results = dataParsed.result.results; + + const userGroupsTempList: UserGroupNew[] = []; + + for (let i = 0; i < count; i++) { + userGroupsTempList.push({ + cn: normalizeString(results[i].result.cn), + gidnumber: normalizeString(results[i].result.gidnumber), + description: normalizeString(results[i].result.description), + dn: results[i].result.dn, + }); + } + setUserGroupsFullList(userGroupsTempList); + } + }, [userGroupsData, userGroupsQuery.isFetching]); + + // [API call] Refresh + const refetch = () => { + userGroupsQuery.refetch(); + }; + + // Return data + return { + isFetching: userGroupsQuery.isFetching, + isLoading: isUserGroupsLoading, + refetch, + userGroupsFullList, + } as MemberOfData; +}; + +export { useUserMemberOfData }; diff --git a/src/services/rpc.ts b/src/services/rpc.ts index 20378b6e8..e6b0318c5 100644 --- a/src/services/rpc.ts +++ b/src/services/rpc.ts @@ -22,6 +22,7 @@ import { UIDType, User, Service, + cnType, } from "../utils/datatypes/globalDataTypes"; import { apiToHost } from "../utils/hostUtils"; import { apiToUser } from "../utils/userUtils"; @@ -141,7 +142,7 @@ export interface GenericPayload { stopIdx: number; objName?: string; objAttr?: string; - entryType?: "user" | "stage" | "preserved" | "host" | "service"; + entryType?: "user" | "stage" | "preserved" | "host" | "service" | "group"; } export interface HostAddPayload { @@ -695,6 +696,8 @@ export const api = createApi({ id = idResponseData.result.result[i] as servicesType; } else if (objName === "user" || objName === "stageuser") { id = idResponseData.result.result[i] as UIDType; + } else if (objName === "group") { + id = idResponseData.result.result[i] as cnType; } else { // Unknown, should never happen return { @@ -1073,6 +1076,16 @@ export const api = createApi({ }); }, }), + getUserByUid: build.query({ + query: (uid) => { + return getCommand({ + method: "user_show", + params: [[uid], { version: API_VERSION_BACKUP }], + }); + }, + transformResponse: (response: FindRPCResponse): User => + response.result.result as unknown as User, + }), }), }); @@ -1116,6 +1129,12 @@ export const useGettingServicesQuery = (payloadData) => { payloadData["objAttr"] = "krbprincipalname"; return useGettingGenericQuery(payloadData); }; +// Groups +export const useGettingGroupsQuery = (payloadData) => { + payloadData["objName"] = "group"; + payloadData["objAttr"] = "cn"; + return useGettingGenericQuery(payloadData); +}; // Full search wrappers export const useGetUsersFullQuery = (userId: string) => { @@ -1182,4 +1201,5 @@ export const { useGetGenericListQuery, useRemoveServicesMutation, useSearchEntriesMutation, + useGetUserByUidQuery, } = api; diff --git a/src/utils/datatypes/globalDataTypes.ts b/src/utils/datatypes/globalDataTypes.ts index 136e71ba4..549109c2c 100644 --- a/src/utils/datatypes/globalDataTypes.ts +++ b/src/utils/datatypes/globalDataTypes.ts @@ -67,7 +67,11 @@ export interface User { ipanthomedirectorydrive: string; // 'Member of' data memberof_group: string[]; // multivalue - memberof_subid?: string[]; // multivalue + memberof_netgroup: string[]; // multivalue + memberof_role: string[]; // multivalue + memberof_hbacrule: string[]; // multivalue + memberof_sudorule: string[]; // multivalue + memberof_subid: string[]; // multivalue // 'Managed by' data mepmanagedentry: string[]; // other @@ -129,6 +133,13 @@ export interface UserGroup { description: string; } +export interface UserGroupNew { + cn: string; + gidnumber: string; + description: string; + dn: string; +} + export interface Netgroup { name: string; description: string; diff --git a/src/utils/userUtils.tsx b/src/utils/userUtils.tsx index d2d9f20d6..d307671ed 100644 --- a/src/utils/userUtils.tsx +++ b/src/utils/userUtils.tsx @@ -161,6 +161,10 @@ export function createEmptyUser(): User { ipanthomedirectorydrive: "", // 'Member of' data memberof_group: [], + memberof_netgroup: [], + memberof_role: [], + memberof_hbacrule: [], + memberof_sudorule: [], memberof_subid: [], // 'Managed by' data mepmanagedentry: [], diff --git a/src/utils/utils.tsx b/src/utils/utils.tsx index 212a288a5..872b9d58e 100644 --- a/src/utils/utils.tsx +++ b/src/utils/utils.tsx @@ -295,3 +295,22 @@ export const isValidIpAddress = (ipAddress: string) => { return regexIPv4.test(ipAddress); } }; + +// Normalize string LDAP values (string[] --> string) +export const normalizeString = (entry: string[] | string) => { + let newValue = ""; + if (entry !== undefined) { + newValue = entry[0]; + } + return newValue; +}; + +// Some values in a table might not have a specific value defined +// (i.e. empty string ""). This is not allowed by the table component. +// Therefore, this function will return "-" instead of "". +export const parseEmptyString = (str: string) => { + if (str === "") { + return "-"; + } + return str; +};