Skip to content

Commit

Permalink
Issue 248 - Add hosts create keytab for users
Browse files Browse the repository at this point in the history
Update the dual selector list to search for entries, and implement the add/delete functionality.

relates: #248

Signed-off-by:  Mark Reynolds <[email protected]>
  • Loading branch information
carma12 authored and mreynolds389 committed Mar 11, 2024
1 parent c3cc12f commit b9deb55
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 52 deletions.
11 changes: 9 additions & 2 deletions src/components/HostsSections/AllowedCreateKeytab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ import CreateKeytabUserGroupsTable from "../tables/HostsSettings/CreateKeytabUse
import CreateKeytabHostsTable from "../tables/HostsSettings/CreateKeytabHostsTable";
import CreateKeytabHostGroupsTable from "../tables/HostsSettings/CreateKeytabHostGroupsTable";
// Data types
import { Host } from "src/utils/datatypes/globalDataTypes";
import { Host } from "../../utils/datatypes/globalDataTypes";

interface PropsToAllowCreateKeytab {
host: Partial<Host>;
onRefresh: () => void;
}

const AllowedCreateKeytab = (props: PropsToAllowCreateKeytab) => {
let fqdn = "";
if (props.host.fqdn !== undefined) {
fqdn = props.host.fqdn;
}

return (
<Flex direction={{ default: "column", lg: "row" }}>
<FlexItem flex={{ default: "flex_1" }}>
<CreateKeytabUsersTable host={fqdn} />
<CreateKeytabUsersTable
from="host"
id={fqdn}
entry={props.host}
onRefresh={props.onRefresh}
/>
<CreateKeytabHostsTable host={fqdn} />
</FlexItem>
<FlexItem flex={{ default: "flex_1" }}>
Expand Down
8 changes: 7 additions & 1 deletion src/components/ServicesSections/AllowedCreateKeytab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import { Service } from "../../utils/datatypes/globalDataTypes";

interface PropsToAllowCreateKeytab {
service: Service;
onRefresh: () => void;
}

const AllowedCreateKeytab = (props: PropsToAllowCreateKeytab) => {
return (
<Flex direction={{ default: "column", lg: "row" }}>
<FlexItem flex={{ default: "flex_1" }}>
<CreateKeytabUsersTable host={props.service.krbcanonicalname} />
<CreateKeytabUsersTable
from="service"
id={props.service.krbcanonicalname}
entry={props.service}
onRefresh={props.onRefresh}
/>
<CreateKeytabHostsTable host={props.service.krbcanonicalname} />
</FlexItem>
<FlexItem flex={{ default: "flex_1" }}>
Expand Down
135 changes: 119 additions & 16 deletions src/components/modals/HostsSettings/CreateKeytabElementsAddModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { Button, DualListSelector } from "@patternfly/react-core";
// Layout
import SecondaryButton from "src/components/layouts/SecondaryButton";
import ModalWithFormLayout from "src/components/layouts/ModalWithFormLayout";
import SearchInputLayout from "../../layouts/SearchInputLayout";
//Icons
import InfoCircleIcon from "@patternfly/react-icons/dist/esm/icons/info-circle-icon";
import ExclamationTriangleIcon from "@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon";
// RPC client
import { useGetIDListMutation, GenericPayload } from "../../../services/rpc";

interface PropsToAddModal {
host: string;
Expand All @@ -20,32 +26,131 @@ interface PropsToAddModal {
}

const CreateKeytabElementsAddModal = (props: PropsToAddModal) => {
// Dual list data
const data = props.availableData;

// Dual list selector
const [availableOptions, setAvailableOptions] = useState<ReactNode[]>(
data.sort()
const initialList = (
<div>
<InfoCircleIcon className="pf-v5-u-info-color-100 pf-v5-u-mr-sm" /> Enter
a value in the search field
</div>
);
const [chosenOptions, setChosenOptions] = useState<ReactNode[]>([]);

const [availableOptions, setAvailableOptions] = useState<
string[] | ReactNode[]
>([initialList]);
const [chosenOptions, setChosenOptions] = useState<string[]>([]);
const [searchValue, setSearchValue] = React.useState("");
const [searchDisabled, setSearchIsDisabled] = useState<boolean>(false);

const updateAvailableOptions = (newList: string[]) => {
if (newList.length === 0) {
const emptyList = (
<div id="disabled">
<ExclamationTriangleIcon className="pf-v5-u-warning-color-100 pf-v5-u-mr-sm" />{" "}
No matching results
</div>
);
setAvailableOptions([emptyList]);
return;
}
// Filter out options already in the table
const filterUsersData = newList.filter((item) => {
return !props.tableElementsList.some((itm) => {
return item === itm;
});
});

// Filter out options that have already been chosen
const cleanList = filterUsersData.filter((item) => {
return !chosenOptions.some((itm) => {
return item === itm;
});
});
setAvailableOptions(cleanList);
};

// Issue a search using a specific search value
const [retrieveIDs] = useGetIDListMutation({});
const submitSearchValue = () => {
setSearchIsDisabled(true);
retrieveIDs({
searchValue: searchValue,
sizeLimit: 200,
startIdx: 0,
stopIdx: 200,
entryType: props.elementType,
} as GenericPayload).then((result) => {
if ("data" in result) {
updateAvailableOptions(result.data.list);
}
setSearchIsDisabled(false);
});
};

const updateSearchValue = (value: string) => {
setSearchValue(value);
};

const searchValueData = {
searchValue: searchValue,
updateSearchValue: updateSearchValue,
submitSearchValue: submitSearchValue,
};

const listChange = (
newAvailableOptions: ReactNode[],
newChosenOptions: ReactNode[]
) => {
setAvailableOptions(newAvailableOptions.sort());
setChosenOptions(newChosenOptions.sort());
// Only "message" options are actually react nodes,
// revise the lists as needed.
for (let idx = 0; idx < newChosenOptions.length; idx++) {
// if not typeof string, remove from list
if (typeof newChosenOptions[idx] !== "string") {
return;
}
}
const newAvailOptions: string[] = [];
for (let idx = 0; idx < newAvailableOptions.length; idx++) {
// Revise avail list to only includue valid string options
if (typeof newAvailableOptions[idx] === "string") {
const option = newAvailableOptions[idx] as string;
newAvailOptions.push(option);
}
}

setAvailableOptions(newAvailOptions.sort() as string[]);
setChosenOptions(newChosenOptions.sort() as string[]);
// The recently added entries are removed from the available data options
props.updateAvailableData(newAvailableOptions.sort());
props.updateAvailableData(newAvailOptions.sort());
};

let availOptions;
if (availableOptions.length === 0) {
// No option, should display some info about this
if (searchValue === "") {
availOptions = [initialList];
}
} else {
availOptions = availableOptions;
}

const fields = [
{
id: "dual-list-search",
pfComponent: (
<SearchInputLayout
name="search"
ariaLabel="Search dual select list"
placeholder="Search for entries"
searchValueData={searchValueData}
isDisabled={searchDisabled}
/>
),
},
{
id: "dual-list-selector",
pfComponent: (
<DualListSelector
isSearchable
availableOptions={availableOptions}
availableOptions={availOptions}
chosenOptions={chosenOptions}
onListChange={(
_event,
Expand All @@ -60,7 +165,7 @@ const CreateKeytabElementsAddModal = (props: PropsToAddModal) => {

// When clean data, set to original values
const cleanData = () => {
setAvailableOptions(data);
setAvailableOptions([]);
setChosenOptions([]);
props.updateSelectedElements([]);
};
Expand All @@ -84,14 +189,12 @@ const CreateKeytabElementsAddModal = (props: PropsToAddModal) => {
// Add element to the list
const addElementToList = () => {
const itemsToAdd: string[] = [];
const newTableElementsList = props.tableElementsList;
chosenOptions.map((opt) => {
if (opt !== undefined && opt !== null) {
itemsToAdd.push(opt.toString());
newTableElementsList.push(opt.toString());
}
});
props.updateTableElementsList(newTableElementsList);
props.updateTableElementsList(itemsToAdd);
props.updateSelectedElements([]);
cleanAndCloseModal();
};
Expand Down Expand Up @@ -125,7 +228,7 @@ const CreateKeytabElementsAddModal = (props: PropsToAddModal) => {
props.elementType +
"s to " +
props.operationType +
" keytab of " +
" keytab for " +
props.host
}
formId={
Expand Down
Loading

0 comments on commit b9deb55

Please sign in to comment.