blob: 513a2c5c58d24c29faee664fc2a736e9aaeb7881 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState, useRef } from "react";
import { Button, Table } from "react-bootstrap";
import { Field } from "react-final-form";
import { FieldArray } from "react-final-form-arrays";
import Select from "react-select";
import AsyncSelect from "react-select/async";
import Editable from "Components/Editable";
import CreatableField from "Components/CreatableField";
import ModalResourceComp from "../Resources/ModalResourceComp";
import { uniq, map, join, isEmpty, isArray } from "lodash";
import TagBasePermissionItem from "../PolicyListing/TagBasePermissionItem";
import { dragStart, dragEnter, drop, dragOver } from "../../utils/XAUtils";
export default function ServiceAuditFilter(props) {
const {
serviceDetails,
serviceDefDetails,
fetchUsersData,
fetchGroupsData,
fetchRolesData,
addAuditFilter,
formValues
} = props;
const dragItem = useRef();
const dragOverItem = useRef();
const [defaultUserOptions, setDefaultUserOptions] = useState([]);
const [defaultGroupOptions, setDefaultGroupOptions] = useState([]);
const [defaultRoleOptions, setDefaultRoleOptions] = useState([]);
const [roleLoading, setRoleLoading] = useState(false);
const [groupLoading, setGroupLoading] = useState(false);
const [userLoading, setUserLoading] = useState(false);
const [modelState, setModalstate] = useState({
showModalResource: false,
resourceInput: null,
data: {}
});
const handleClose = () => {
setModalstate({
showModalResource: false,
resourceInput: null,
data: {}
});
};
const renderResourcesModal = (input) => {
setModalstate({
showModalResource: true,
resourceInput: input,
data: isEmpty(input.value) ? {} : input.value
});
};
const handleSave = () => {
let inputData;
inputData = modelState.data;
modelState.resourceInput.onChange(inputData);
handleClose();
};
const handleRemove = (input) => {
for (const obj in input.value) {
delete input.value[obj];
}
setModalstate({
showModalResource: false,
resourceInput: null,
data: {}
});
};
const getResourceData = (resourceData) => {
let dataStructure = [];
let levels = uniq(map(serviceDefDetails?.resources, "level"));
dataStructure = levels.map((level, index) => {
if (
resourceData[`resourceName-${level}`] !== undefined &&
resourceData[`value-${level}`] !== undefined
) {
let excludesSupported =
resourceData[`resourceName-${level}`].excludesSupported;
let recursiveSupported =
resourceData[`resourceName-${level}`].recursiveSupported;
return (
<div className="resource-filter" key={index}>
<div>
<span className="bold me-1">
{resourceData[`resourceName-${level}`].name}
</span>
:
<span className="ms-1">
{isArray(resourceData[`value-${level}`])
? join(map(resourceData[`value-${level}`], "value"), ", ")
: [resourceData[`value-${level}`].value]}
</span>
</div>
<div>
{excludesSupported && (
<h6 className="text-center">
{resourceData[`isExcludesSupport-${level}`] == false ? (
<span className="badge bg-dark">Exclude</span>
) : (
<span className="badge bg-dark">Include</span>
)}
</h6>
)}
{recursiveSupported && (
<h6 className="text-center">
{resourceData[`isRecursiveSupport-${level}`] == false ? (
<span className="badge bg-dark">Non Recursive</span>
) : (
<span className="badge bg-dark">Recursive</span>
)}
</h6>
)}
</div>
</div>
);
}
});
return dataStructure;
};
const getResourceIcon = (resourceData) => {
let iconClass = !isEmpty(resourceData)
? "fa fa-fw fa-pencil"
: "fa fa-fw fa-plus";
return iconClass;
};
const getAccessTypeOptions = () => {
let srcOp = [];
srcOp = serviceDefDetails?.accessTypes;
return srcOp?.map(({ label, name: value }) => ({
label,
value
}));
};
const permList = [
"Is Audited",
"Access Result",
"Resources",
"Operations",
"Permissions",
"Users",
"Groups",
"Roles",
" "
];
const tableHeader = () => {
return permList.map((data) => {
return <th key={data}>{data}</th>;
});
};
const handleSelectChange = (value, input) => {
input.onChange(value);
};
const onFocusRoleSelect = () => {
setRoleLoading(true);
fetchRolesData().then((opts) => {
setDefaultRoleOptions(opts);
setRoleLoading(false);
});
};
const onFocusGroupSelect = () => {
setGroupLoading(true);
fetchGroupsData().then((opts) => {
setDefaultGroupOptions(opts);
setGroupLoading(false);
});
};
const onFocusUserSelect = () => {
setUserLoading(true);
fetchUsersData().then((opts) => {
setDefaultUserOptions(opts);
setUserLoading(false);
});
};
return (
<div className="table-responsive">
<Table
bordered
size="sm"
className="mt-3 table-audit-filter text-center d-table"
>
<thead>
<tr>{tableHeader()}</tr>
</thead>
<tbody className="drag-drop-wrap">
{formValues.auditFilters !== undefined &&
formValues.auditFilters.length === 0 && (
<tr className="text-center">
<td colSpan={permList.length}>
Click on below <i className="fa-fw fa fa-plus"></i>
button to add audit filter !!
</td>
</tr>
)}
<FieldArray name="auditFilters">
{({ fields }) =>
fields.map((name, index) => (
<tr
key={name}
onDragStart={(e) => dragStart(e, index, dragItem)}
onDragEnter={(e) => dragEnter(e, index, dragOverItem)}
onDragEnd={(e) => drop(e, fields, dragItem, dragOverItem)}
onDragOver={(e) => dragOver(e)}
draggable
id={index}
>
{permList.map((colName) => {
if (colName == "Is Audited") {
return (
<td key={`${name}.isAudited`} className="align-middle">
<div className="d-flex">
<Field
className="form-control audit-filter-select"
name={`${name}.isAudited`}
component="select"
style={{ minWidth: "75px" }}
>
<option value="true">Yes</option>
<option value="false">No</option>
</Field>
</div>
</td>
);
}
if (colName == "Access Result") {
return (
<td key={colName} className="align-middle">
<Field
className="form-control"
name={`${name}.accessResult`}
>
{({ input }) => (
<div style={{ minWidth: "195px" }}>
<Select
{...input}
menuPortalTarget={document.body}
isClearable={true}
isSearchable={false}
options={[
{ value: "DENIED", label: "DENIED" },
{ value: "ALLOWED", label: "ALLOWED" },
{
value: "NOT_DETERMINED",
label: "NOT_DETERMINED"
}
]}
menuPlacement="auto"
placeholder="Select Value"
/>
</div>
)}
</Field>
</td>
);
}
if (colName == "Resources") {
return (
<td key={`${name}.resources`} className="align-middle">
<Field
name={`${name}.resources`}
render={({ input }) => (
<React.Fragment>
<div style={{ minWidth: "195px" }}>
<div className="resource-group">
{getResourceData(input.value)}
</div>
<Button
className="me-1 btn-mini"
variant="primary"
size="sm"
onClick={() => renderResourcesModal(input)}
data-js="serviceResource"
data-cy="serviceResource"
>
<i
className={getResourceIcon(input.value)}
></i>
</Button>
<Button
className="me-1 btn-mini"
variant="danger"
size="sm"
onClick={() => handleRemove(input)}
>
<i className="fa-fw fa fa-remove"></i>
</Button>
</div>
</React.Fragment>
)}
/>
</td>
);
}
if (colName == "Operations") {
return (
<td
className="mx-w-210 align-middle"
key={`${name}.actions`}
width="210px"
>
<Field
className="form-control"
name={`${name}.actions`}
>
{({ input }) => (
<CreatableField
actionValues={input.value}
creatableOnChange={(value) =>
handleSelectChange(value, input)
}
/>
)}
</Field>
</td>
);
}
if (colName == "Permissions") {
if (serviceDefDetails?.name == "tag") {
return (
<td
key={`${name}.accessTypes`}
className="align-middle"
>
<Field
className="form-control"
name={`${name}.accessTypes`}
render={({ input }) => (
<TagBasePermissionItem
options={getAccessTypeOptions()}
inputVal={input}
/>
)}
/>
</td>
);
} else {
return (
<td
key={`${name}.accessTypes`}
className="align-middle"
>
<Field
className="form-control"
name={`${name}.accessTypes`}
render={({ input }) => (
<div style={{ minWidth: "100px" }}>
<Editable
{...input}
placement="right"
type="checkbox"
options={getAccessTypeOptions()}
showSelectAll={true}
selectAllLabel="Select All"
/>
</div>
)}
/>
</td>
);
}
}
if (colName == "Roles") {
return (
<td key={`${name}.roles`} className="align-middle">
<Field
className="form-control"
name={`${name}.roles`}
render={({ input }) => (
<div
style={{
minWidth: "150px",
maxWidth: "350px"
}}
>
<AsyncSelect
{...input}
menuPortalTarget={document.body}
components={{
IndicatorSeparator: () => null
}}
loadOptions={fetchRolesData}
onFocus={() => {
onFocusRoleSelect();
}}
defaultOptions={defaultRoleOptions}
isClearable={false}
noOptionsMessage={() =>
roleLoading ? "Loading..." : "No options"
}
menuPlacement="auto"
cacheOptions
isMulti
/>
</div>
)}
/>
</td>
);
}
if (colName == "Groups") {
return (
<td key={`${name}.groups`} className="align-middle">
<Field
className="form-control"
name={`${name}.groups`}
render={({ input }) => (
<div
style={{
minWidth: "150px",
maxWidth: "350px"
}}
>
<AsyncSelect
{...input}
menuPortalTarget={document.body}
components={{
IndicatorSeparator: () => null
}}
loadOptions={fetchGroupsData}
onFocus={() => {
onFocusGroupSelect();
}}
defaultOptions={defaultGroupOptions}
isClearable={false}
menuPlacement="auto"
noOptionsMessage={() =>
groupLoading ? "Loading..." : "No options"
}
cacheOptions
isMulti
/>
</div>
)}
/>
</td>
);
}
if (colName == "Users") {
return (
<td key={`${name}.users`} className="align-middle">
<Field
className="form-control"
name={`${name}.users`}
render={({ input }) => (
<div
style={{
minWidth: "150px",
maxWidth: "350px"
}}
>
<AsyncSelect
{...input}
key={input.name}
menuPortalTarget={document.body}
components={{
IndicatorSeparator: () => null
}}
loadOptions={fetchUsersData}
onFocus={() => {
onFocusUserSelect();
}}
defaultOptions={defaultUserOptions}
isClearable={false}
menuPlacement="auto"
cacheOptions
noOptionsMessage={() =>
userLoading ? "Loading..." : "No options"
}
isMulti
/>
</div>
)}
/>
</td>
);
}
})}
<td key={`${index}.remove`} className="align-middle">
<Button
variant="danger"
size="sm"
title="Remove"
onClick={() => fields.remove(index)}
data-action="delete"
data-cy="delete"
>
<i className="fa-fw fa fa-remove"></i>
</Button>
</td>
</tr>
))
}
</FieldArray>
</tbody>
</Table>
<Button
variant="outline-dark"
size="sm"
className="btn-sm"
type="button"
onClick={() => addAuditFilter("auditFilters", undefined)}
data-action="addGroup"
data-cy="addGroup"
title="Add"
>
<i className="fa-fw fa fa-plus"></i>
</Button>
<ModalResourceComp
serviceDetails={serviceDetails}
serviceCompDetails={serviceDefDetails}
cancelButtonText="Cancel"
actionButtonText="Submit"
handleSave={handleSave}
modelState={modelState}
handleClose={handleClose}
/>
</div>
);
}