This is an automated email from the ASF dual-hosted git repository.
dineshkumar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 34ab10570 RANGER-4119 - Improvement in policy condition on Ranger
Admin UI
34ab10570 is described below
commit 34ab1057084e966c581ee785c7f34dbbb6180044
Author: Dhaval Rajpara <[email protected]>
AuthorDate: Tue Jul 25 10:11:42 2023 +0530
RANGER-4119 - Improvement in policy condition on Ranger Admin UI
Signed-off-by: Dineshkumar Yadav <[email protected]>
---
.../service-defs/ranger-servicedef-abfs.json | 3 +-
.../service-defs/ranger-servicedef-kafka.json | 4 +-
.../service-defs/ranger-servicedef-knox.json | 4 +-
.../service-defs/ranger-servicedef-ozone.json | 4 +-
.../ranger-servicedef-schema-registry.json | 4 +-
.../service-defs/ranger-servicedef-solr.json | 4 +-
.../service-defs/ranger-servicedef-tag.json | 3 +-
.../react-webapp/src/components/Editable.jsx | 502 +++++++++++++--------
.../main/webapp/react-webapp/src/utils/XAUtils.js | 19 +
.../AuditEvent/AdminLogs/PolicyViewDetails.jsx | 29 +-
.../views/PolicyListing/AddUpdatePolicyForm.jsx | 75 ++-
.../views/PolicyListing/PolicyConditionsComp.jsx | 194 ++++----
.../views/PolicyListing/PolicyPermissionItem.jsx | 126 +++---
.../views/ServiceManager/ServiceAuditFilter.jsx | 3 +-
.../src/views/ServiceManager/ServiceForm.jsx | 3 +-
15 files changed, 556 insertions(+), 421 deletions(-)
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json
index 7dcf38895..879df6b9f 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json
@@ -118,7 +118,8 @@
"name": "ip-range",
"evaluator":
"org.apache.ranger.plugin.conditionevaluator.RangerIpMatcher",
"label": "IP Address Range",
- "description": "IP Address Range"
+ "description": "IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
}
\ No newline at end of file
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
index 2f511eff5..b169c6ba9 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
@@ -239,9 +239,9 @@
},
"validationRegEx":"",
"validationMessage":"",
- "uiHint":"",
"label":"IP Address Range",
- "description":"IP Address Range"
+ "description":"IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
}
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json
index 410b9ef58..ee44687b4 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json
@@ -135,9 +135,9 @@
"evaluatorOptions": { },
"validationRegEx":"",
"validationMessage": "",
- "uiHint":"",
"label": "IP Address Range",
- "description": "IP Address Range"
+ "description": "IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
}
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
index 4b899736b..13915130c 100755
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
@@ -254,9 +254,9 @@
"evaluatorOptions": { },
"validationRegEx":"",
"validationMessage": "",
- "uiHint":"",
"label": "IP Address Range",
- "description": "IP Address Range"
+ "description": "IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json
index 987a50fc5..bdebf406d 100644
---
a/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json
+++
b/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json
@@ -264,9 +264,9 @@
},
"validationRegEx":"",
"validationMessage":"",
- "uiHint":"",
"label":"IP Address Range",
- "description":"IP Address Range"
+ "description":"IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json
index de3399845..50545f744 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json
@@ -188,9 +188,9 @@
},
"validationRegEx":"",
"validationMessage":"",
- "uiHint":"",
"label":"IP Address Range",
- "description":"IP Address Range"
+ "description":"IP Address Range",
+ "uiHint" : "{ \"isMultiValue\":true }"
}
]
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json
index 7cb523075..add29feed 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json
@@ -88,7 +88,8 @@
"evaluator":
"org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator",
"evaluatorOptions" : {"engineName":"JavaScript",
"ui.isMultiline":"true"},
"label":"Enter boolean expression",
- "description": "Boolean expression"
+ "description": "Boolean expression",
+ "uiHint" : "{ \"isMultiline\":true }"
}
]
}
diff --git
a/security-admin/src/main/webapp/react-webapp/src/components/Editable.jsx
b/security-admin/src/main/webapp/react-webapp/src/components/Editable.jsx
index 7dd32ebaa..34eb8c5fa 100644
--- a/security-admin/src/main/webapp/react-webapp/src/components/Editable.jsx
+++ b/security-admin/src/main/webapp/react-webapp/src/components/Editable.jsx
@@ -17,7 +17,13 @@
* under the License.
*/
-import React, { useEffect, useReducer, useRef, useState, useCallback } from
"react";
+import React, {
+ useEffect,
+ useReducer,
+ useRef,
+ useState,
+ useCallback
+} from "react";
import {
OverlayTrigger,
Popover,
@@ -27,13 +33,14 @@ import {
Col,
Badge
} from "react-bootstrap";
-import { findIndex, isArray } from "lodash";
+import { find, findIndex, isArray, isEmpty, map } from "lodash";
import { isObject } from "Utils/XAUtils";
import CreatableSelect from "react-select/creatable";
import Select from "react-select";
import { InfoIcon } from "../utils/XAUtils";
import { RegexMessage } from "../utils/XAMessages";
+const esprima = require("esprima");
const TYPE_SELECT = "select";
const TYPE_CHECKBOX = "checkbox";
const TYPE_INPUT = "input";
@@ -75,7 +82,7 @@ const CheckboxComp = (props) => {
return (
<>
- {options.map((obj) => (
+ {options.map((obj, index) => (
<Form.Group className="mb-3" controlId={obj.label} key={obj.label}>
<Form.Check
checked={isChecked(obj)}
@@ -146,140 +153,171 @@ const InputboxComp = (props) => {
);
};
-const CreatableSelectNew = (props) => {
- const { value, valRef, conditionDefVal, selectProps } = props;
- const [selectedInputVal, setSelectVal] = useState(value);
- const handleChange = (e) => {
- valRef.current = e;
- setSelectVal(e);
- };
-
- return (
- <>
- <Form.Group className="mb-3" controlId="Ip-range">
- <b>{conditionDefVal.label}:</b>
- <CreatableSelect
- {...selectProps}
- defaultValue={
- selectedInputVal == ""
- ? null
- : !isArray(selectedInputVal)
- ? selectedInputVal["ip-range"]
- .split(", ")
- .map((obj) => ({ label: obj, value: obj }))
- : selectedInputVal
- }
- onChange={(e) => handleChange(e)}
- placeholder="enter expression"
- width="500px"
- />
- </Form.Group>
- </>
- );
-};
-
const CustomCondition = (props) => {
- const { value, valRef, conditionDefVal, selectProps } = props;
- const accessedOpt = [
- { value: "yes", label: "Yes" },
- { value: "no", label: "No" }
- ];
- const [selectedCondVal, setCondSelect] = useState(
- value?.["accessed-after-expiry"] || value
- );
- const [selectedJSCondVal, setJSCondVal] = useState(
- value?.expression || value
- );
+ const { value, valRef, conditionDefVal, selectProps, validExpression } =
+ props;
const tagAccessData = (val, key) => {
if (!isObject(valRef.current)) {
valRef.current = {};
}
valRef.current[key] = val;
};
- const selectHandleChange = (e, name) => {
- setCondSelect(e);
- tagAccessData(e?.value || null, name);
- };
- const textAreaHandleChange = (e, name) => {
- setJSCondVal(e.target.value);
- tagAccessData(e.target.value, name);
- };
- const accessedVal = (val) => {
- let value = null;
- if (val) {
- let opObj = accessedOpt.filter((m) => {
- if (m.value == val) {
- return m;
- }
- });
- if (opObj) {
- value = opObj;
- }
- }
- return value;
- };
- const expressionVal = (val) => {
- let value = null;
- if(val?.expression !== undefined){
- return value = val.expression;
- }
- if(val != "" && typeof(val) != "object"){
- return value = val
- }
- return value;
- };
+
return (
<>
{conditionDefVal?.length > 0 &&
conditionDefVal.map((m, index) => {
- if (m.name == "accessed-after-expiry") {
- return (
- <Form.Group className="mb-3">
- <b>{m.label}:</b>
- <Select
- options={accessedOpt}
- isClearable
- onChange={(e) => selectHandleChange(e, m.name)}
- value={
- selectedCondVal?.value
- ? accessedVal(selectedCondVal.value)
- : accessedVal(selectedCondVal)
+ let uiHintAttb =
+ m.uiHint != undefined && m.uiHint != "" ? JSON.parse(m.uiHint) :
"";
+ if (uiHintAttb != "") {
+ if (uiHintAttb?.singleValue) {
+ const [selectedCondVal, setCondSelect] = useState(
+ value?.[m.name] || value
+ );
+ const accessedOpt = [
+ { value: "yes", label: "Yes" },
+ { value: "no", label: "No" }
+ ];
+ const accessedVal = (val) => {
+ let value = null;
+ if (val) {
+ let opObj = accessedOpt?.filter((m) => {
+ if (m.value == (val[0]?.value || val)) {
+ return m;
+ }
+ });
+ if (opObj) {
+ value = opObj;
}
- />
- </Form.Group>
- );
- }
- if (m.name == "expression") {
- return (
- <>
- <Form.Group className="mb-3">
- <Row>
- <Col>
- <b>{m.label}:</b>
- <InfoIcon
- position="right"
- message={
- <p className="pd-10">
- {RegexMessage.MESSAGE.policyconditioninfoicon}
- </p>
- }
- />
- </Col>
- </Row>
- <Row>
- <Col>
- <Form.Control
- as="textarea"
- rows={3}
- // value={selectedJSCondVal?.expression !== undefined
? selectedJSCondVal.expression : selectedJSCondVal}
- value={expressionVal(selectedJSCondVal)}
- onChange={(e) => textAreaHandleChange(e, m.name)}
- />
- </Col>
- </Row>
- </Form.Group>
- </>
- );
+ }
+ return value;
+ };
+ const selectHandleChange = (e, name) => {
+ let filterVal = accessedOpt?.filter((m) => {
+ if (m.value != e[0]?.value) {
+ return m;
+ }
+ });
+ setCondSelect(
+ !isEmpty(e) ? (e?.length > 1 ? filterVal : e) : null
+ );
+ tagAccessData(
+ !isEmpty(e)
+ ? e?.length > 1
+ ? filterVal[0].value
+ : e[0].value
+ : null,
+ name
+ );
+ };
+ return (
+ <div key={m.name}>
+ <Form.Group className="mb-3">
+ <b>{m.label}:</b>
+ <Select
+ options={accessedOpt}
+ onChange={(e) => selectHandleChange(e, m.name)}
+ value={
+ selectedCondVal?.value
+ ? accessedVal(selectedCondVal.value)
+ : accessedVal(selectedCondVal)
+ }
+ isMulti={true}
+ isClearable={false}
+ />
+ </Form.Group>
+ </div>
+ );
+ }
+ if (uiHintAttb?.isMultiline) {
+ const [selectedJSCondVal, setJSCondVal] = useState(
+ value?.[m.name] || value
+ );
+ const expressionVal = (val) => {
+ let value = null;
+ if (val != "" && typeof val != "object") {
+ valRef.current[m.name] = val;
+ return (value = val);
+ }
+ return value !== null ? value : "";
+ };
+ const textAreaHandleChange = (e, name) => {
+ setJSCondVal(e.target.value);
+ tagAccessData(e.target.value, name);
+ };
+ return (
+ <div key={m.name}>
+ <Form.Group className="mb-3">
+ <Row>
+ <Col>
+ <b>{m.label}:</b>
+ <InfoIcon
+ position="right"
+ message={
+ <p className="pd-10">
+ {RegexMessage.MESSAGE.policyconditioninfoicon}
+ </p>
+ }
+ />
+ </Col>
+ </Row>
+ <Row>
+ <Col>
+ <Form.Control
+ as="textarea"
+ rows={3}
+ key={m.name}
+ value={expressionVal(selectedJSCondVal)}
+ onChange={(e) => textAreaHandleChange(e, m.name)}
+ isInvalid={validExpression.state}
+ />
+ {validExpression.state && (
+ <div className="text-danger">
+ {validExpression.errorMSG}
+ </div>
+ )}
+ </Col>
+ </Row>
+ </Form.Group>
+ </div>
+ );
+ }
+ if (uiHintAttb?.isMultiValue) {
+ const [selectedInputVal, setSelectVal] = useState(
+ value?.[m.name] || []
+ );
+ const handleChange = (e, name) => {
+ setSelectVal(e);
+ tagAccessData(e, name);
+ };
+ return (
+ <div key={m.name}>
+ <Form.Group
+ className="mb-3"
+ controlId="Ip-range"
+ key={m.name}
+ >
+ <b>{m.label}:</b>
+ <CreatableSelect
+ {...selectProps}
+ defaultValue={
+ selectedInputVal == ""
+ ? null
+ : !isArray(selectedInputVal)
+ ? selectedInputVal
+ ?.split(", ")
+ .map((obj) => ({ label: obj, value: obj }))
+ : selectedInputVal
+ }
+ onChange={(e) => handleChange(e, m.name)}
+ placeholder="enter expression"
+ width="500px"
+ isClearable={false}
+ />
+ </Form.Group>
+ </div>
+ );
+ }
}
})}
</>
@@ -339,35 +377,75 @@ const Editable = (props) => {
const initialLoad = useRef(true);
const popoverRef = useRef(null);
const selectValRef = useRef(null);
+ const [validExpression, setValidated] = useState({
+ state: false,
+ errorMSG: ""
+ });
const [state, dispatch] = useReducer(reducer, props, innitialState);
const { show, value, target } = state;
let isListenerAttached = false;
-
+
const handleClickOutside = (e) => {
- if (document.getElementById("popover-basic")?.contains(e?.target) ==
false) {
- dispatch({
- type: "SET_POPOVER",
- show: false,
- target:null
- });
- }
- e?.stopPropagation()
+ if (
+ document.getElementById("popover-basic")?.contains(e?.target) == false
+ ) {
+ dispatch({
+ type: "SET_POPOVER",
+ show: false,
+ target: null
+ });
+ }
+ e?.stopPropagation();
};
-
+
useEffect(() => {
- if(!isListenerAttached){
- document?.addEventListener('mousedown', handleClickOutside);
+ if (!isListenerAttached) {
+ document?.addEventListener("mousedown", handleClickOutside);
isListenerAttached = true;
return;
- }
- return () => {
- document?.removeEventListener('mousedown', handleClickOutside);
+ }
+ return () => {
+ document?.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const displayValue = () => {
let val = "--";
const selectVal = value;
+ const policyConditionDisplayValue = () => {
+ let ipRangVal, uiHintVal;
+ if (selectVal) {
+ return _.sortBy(Object.keys(selectVal)).map((property, index) => {
+ let conditionObj = find(conditionDefVal, function (m) {
+ if (m.name == property) {
+ return m;
+ }
+ });
+ if (conditionObj?.uiHint && conditionObj?.uiHint != "") {
+ uiHintVal = JSON.parse(conditionObj.uiHint);
+ }
+ if (isArray(selectVal[property])) {
+ ipRangVal = selectVal[property]
+ ?.map(function (m) {
+ return m.value;
+ })
+ .join(", ");
+ }
+ return (
+ <div
+ key={property}
+ className={`${
+ uiHintVal?.isMultiline ? "editable-label" : "badge badge-dark"
+ }`}
+ >
+ {`${conditionObj.label}: ${
+ isArray(selectVal[property]) ? ipRangVal : selectVal[property]
+ }`}
+ </div>
+ );
+ });
+ }
+ };
if (displayFormat) {
val = displayFormat(selectVal);
} else {
@@ -377,10 +455,10 @@ const Editable = (props) => {
servicedefName == "knox" && !isArray(selectVal)
? selectVal["ip-range"]
: selectVal
- .map(function (m) {
- return m.value;
- })
- .join(", ");
+ .map(function (m) {
+ return m.value;
+ })
+ .join(", ");
val = (
<h6 className="d-inline mr-1">
<span
@@ -420,20 +498,20 @@ const Editable = (props) => {
selectVal && selectVal?.length > 0 ? (
<>
<span className="editable-edit-text">
- {selectVal.map((op, index) => (
+ {selectVal.map((op, index) => (
<h6 className="d-inline mr-1" key={index}>
- <Badge variant="info">{op.label}</Badge>
+ <Badge variant="info">{op.label}</Badge>
</h6>
- ))}
+ ))}
</span>
- <Button
- className="mg-10 mx-auto d-block btn-mini"
- variant="outline-dark"
- size="sm"
- type="button"
- >
- <i className="fa-fw fa fa-pencil"></i>
- </Button>
+ <Button
+ className="mg-10 mx-auto d-block btn-mini"
+ variant="outline-dark"
+ size="sm"
+ type="button"
+ >
+ <i className="fa-fw fa fa-pencil"></i>
+ </Button>
</>
) : (
<div className="text-center">
@@ -458,20 +536,21 @@ const Editable = (props) => {
} else if (type === TYPE_INPUT) {
val =
selectVal && selectVal !== "" ? (
- <>
- <span className="editable-edit-text">
+ <>
+ <span className="editable-edit-text">
<h6 className="d-inline mr-1">
- <Badge variant="info">{selectVal}</Badge>
+ <Badge variant="info">{selectVal}</Badge>
</h6>
- </span>
- <Button
- className="mg-10 mx-auto d-block btn-mini"
- variant="outline-dark"
- size="sm"
- type="button">
- <i className="fa-fw fa fa-pencil"></i>
- </Button>
- </>
+ </span>
+ <Button
+ className="mg-10 mx-auto d-block btn-mini"
+ variant="outline-dark"
+ size="sm"
+ type="button"
+ >
+ <i className="fa-fw fa fa-pencil"></i>
+ </Button>
+ </>
) : (
<div className="text-center">
<span className="editable-add-text">Add Row Filter</span>
@@ -488,12 +567,12 @@ const Editable = (props) => {
} else if (type === TYPE_RADIO) {
val =
selectVal && selectVal?.label ? (
- <>
- <span className="editable-edit-text">
+ <>
+ <span className="editable-edit-text">
<h6 className="d-inline mr-1">
- <Badge variant="info">{selectVal.label}</Badge>
+ <Badge variant="info">{selectVal.label}</Badge>
</h6>
- </span>
+ </span>
<Button
className="mg-10 mx-auto d-block btn-mini"
variant="outline-dark"
@@ -502,7 +581,7 @@ const Editable = (props) => {
>
<i className="fa-fw fa fa-pencil"></i>
</Button>
- </>
+ </>
) : (
<div className="text-center">
<span className="editable-add-text">Select Masking Option</span>
@@ -517,16 +596,15 @@ const Editable = (props) => {
</div>
);
} else if (type === TYPE_CUSTOM) {
- if (selectVal?.["accessed-after-expiry"] || selectVal?.expression) {
+ for (const key in selectVal) {
+ if (selectVal[key] == null || selectVal[key] == "") {
+ delete selectVal[key];
+ }
+ }
+ if (Object.keys(selectVal).length != 0) {
val = (
<h6>
- {(selectVal?.["accessed-after-expiry"] !== undefined &&
selectVal?.["accessed-after-expiry"] !== null) &&
- <div className="badge badge-dark">
- {`Accessed after expiry_date (yes/no) :
${selectVal?.["accessed-after-expiry"]}`}
- </div>}
- {(selectVal?.expression !== undefined && selectVal?.expression
!== "") &&
- <div className="editable-label">{`Boolean expression :
${selectVal?.expression}`}
- </div>}
+ {policyConditionDisplayValue()}
<Button
className="mg-10 mx-auto d-block btn-mini"
variant="outline-dark"
@@ -571,21 +649,54 @@ const Editable = (props) => {
} else {
initialLoad.current = false;
}
- type === TYPE_CUSTOM ? selectValRef.current = {...editableValue} :
- selectValRef.current = editableValue;
+ type === TYPE_CUSTOM
+ ? (selectValRef.current = { ...editableValue })
+ : (selectValRef.current = editableValue);
}, [editableValue]);
- const handleApply = () => {
- dispatch({
- type: "SET_VALUE",
- value: selectValRef.current,
- show: !show,
- target: null
- });
- onChange(selectValRef.current);
+ const handleApply = (e) => {
+ let errors, uiHintVal;
+ if (selectValRef?.current) {
+ _.sortBy(Object.keys(selectValRef.current)).map((property) => {
+ let conditionObj = find(conditionDefVal, function (m) {
+ if (m.name == property) {
+ return m;
+ }
+ });
+ if (conditionObj != undefined && conditionObj?.uiHint != "") {
+ uiHintVal = JSON.parse(conditionObj.uiHint);
+ if (
+ uiHintVal?.isMultiline &&
+ selectValRef.current[conditionObj.name] != "" &&
+ selectValRef.current[conditionObj.name] != undefined
+ ) {
+ try {
+ let t = esprima.parseScript(
+ selectValRef.current[conditionObj.name]
+ );
+ } catch (e) {
+ errors = e.message;
+ }
+ }
+ }
+ });
+ }
+ if (errors) {
+ setValidated({ state: true, errorMSG: errors });
+ } else {
+ setValidated({ state: false, errorMSG: "" });
+ dispatch({
+ type: "SET_VALUE",
+ value: selectValRef.current,
+ show: !show,
+ target: null
+ });
+ onChange(selectValRef.current);
+ }
};
- const handleClose = () => {
+ const handleClose = (e) => {
+ setValidated({ state: false, errorMSG: "" });
dispatch({
type: "SET_POPOVER",
show: !show,
@@ -596,8 +707,9 @@ const Editable = (props) => {
const popoverComp = (
<Popover
id="popover-basic"
- className={`editable-popover ${type === TYPE_CHECKBOX &&
"popover-maxHeight"
- }`}
+ className={`editable-popover ${
+ type === TYPE_CHECKBOX && "popover-maxHeight"
+ }`}
>
<Popover.Title>
{type === TYPE_CHECKBOX ? "Select" : "Enter"}
@@ -628,6 +740,7 @@ const Editable = (props) => {
valRef={selectValRef}
conditionDefVal={props.conditionDefVal}
selectProps={props.selectProps}
+ validExpression={validExpression}
/>
) : null}
</Popover.Content>
@@ -653,6 +766,7 @@ const Editable = (props) => {
);
const handleClick = (e) => {
+ setValidated({ state: false, errorMSG: "" });
let display = !show;
dispatch({
type: "SET_POPOVER",
@@ -660,7 +774,7 @@ const Editable = (props) => {
target: e.target
});
};
-
+
return (
<div ref={popoverRef}>
<OverlayTrigger
diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
index 80ce6acfe..7edee019e 100644
--- a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
@@ -1417,3 +1417,22 @@ export const requestDataTitle = (serviceType) => {
}
return title;
};
+
+//Policy condition evaluation
+
+export const policyConditionUpdatedJSON = (policyCond) => {
+ let newPolicyConditionJSON = [...policyCond];
+ newPolicyConditionJSON.filter(function (key, val) {
+ if (!key?.uiHint || key?.uiHint == "") {
+ if (
+ key.evaluatorOptions &&
+ key.evaluatorOptions?.["ui.isMultiline"] == "true"
+ ) {
+ key["uiHint"] = '{ "isMultiline":true }';
+ } else {
+ key["uiHint"] = '{ "isMultiValue":true }';
+ }
+ }
+ });
+ return newPolicyConditionJSON;
+};
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AdminLogs/PolicyViewDetails.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AdminLogs/PolicyViewDetails.jsx
index 7978efda0..7d18afbcc 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AdminLogs/PolicyViewDetails.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AdminLogs/PolicyViewDetails.jsx
@@ -456,13 +456,17 @@ export function PolicyViewDetails(props) {
<td className="text-center">
{!isEmpty(items.conditions)
? items.conditions.map((obj, index) => {
+ let conditionObj =
+ filterServiceDef.policyConditions.find((e) => {
+ return e.name == obj.type;
+ });
return (
<h6 className="d-inline mr-1" key={index}>
<Badge
variant="info"
className="d-inline mr-1"
key={obj.values}
- >{`${obj.type}: ${obj.values.join(
+ >{`${conditionObj.label}: ${obj.values.join(
", "
)}`}</Badge>
</h6>
@@ -559,25 +563,12 @@ export function PolicyViewDetails(props) {
<div className="overflow-auto">
<Table bordered size="sm"
className="table-audit-filter-ready-only">
<tbody>
- {serviceType == "tag" ? (
- conditions.map((obj) => (
- <tr key={obj.type} colSpan="2">
- <td width="40%">{getConditionLabel(obj.type)}</td>
- <td width="60% text-truncate">{obj.values}</td>
- </tr>
- ))
- ) : (
- <tr colSpan="2">
- <td width="20%">
- {filterServiceDef.policyConditions.map(
- (obj) => obj.label
- )}
- </td>
- <td className="text-left">
- {conditions.map((val) => val.values).join("")}
- </td>
+ {conditions.map((obj) => (
+ <tr key={obj.type} colSpan="2">
+ <td width="40%">{getConditionLabel(obj.type)}</td>
+ <td width="60% text-truncate">{obj.values.join(", ")}</td>
</tr>
- )}
+ ))}
</tbody>
</Table>
</div>
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
index 13a8eaf4f..51df2e378 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
@@ -53,7 +53,7 @@ import PolicyPermissionItem from
"../PolicyListing/PolicyPermissionItem";
import { useParams, useNavigate, useLocation } from "react-router-dom";
import PolicyValidityPeriodComp from "./PolicyValidityPeriodComp";
import PolicyConditionsComp from "./PolicyConditionsComp";
-import { getAllTimeZoneList } from "Utils/XAUtils";
+import { getAllTimeZoneList, policyConditionUpdatedJSON } from "Utils/XAUtils";
import moment from "moment";
import {
InfoIcon,
@@ -492,39 +492,22 @@ export default function AddUpdatePolicyForm(props) {
obj.dataMaskInfo.valueExpr = key.dataMaskInfo.valueExpr;
}
}
-
- if (
- key?.conditions &&
- isObject(key.conditions) &&
- serviceCompDetails.name == "tag"
- ) {
+ if (key?.conditions) {
obj.conditions = [];
- Object.entries(key.conditions).map(([key, value]) => {
- if (!isEmpty(value)) {
+ Object.entries(key.conditions).map(
+ ([conditionKey, conditionValue]) => {
return obj.conditions.push({
- type: key,
- values: value?.split(", ")
- });
- }
- });
- } else if (
- !isEmpty(key?.conditions) &&
- isObject(key.conditions) &&
- serviceCompDetails.name == "knox"
- ) {
- obj.conditions = [
- {
- type: "ip-range",
- values:
- !isEmpty(Object.keys(key.conditions)) &&
- !isArray(key.conditions)
- ? key.conditions["ip-range"]?.split(", ")
- : key.conditions.map((value) => {
- return value.value;
+ type: conditionKey,
+ values: !isArray(conditionValue)
+ ? conditionValue?.split(",")
+ : conditionValue.map((m) => {
+ return m.value;
})
+ });
}
- ];
+ );
}
+
if (
!isEmpty(obj) &&
!isEmpty(obj?.delegateAdmin) &&
@@ -730,8 +713,8 @@ export default function AddUpdatePolicyForm(props) {
/*Policy Condition*/
if (values?.conditions) {
+ data.conditions = [];
Object.entries(values.conditions).map(([key, value]) => {
- data.conditions = [];
return data.conditions.push({
type: key,
values: value?.split(",")
@@ -1329,9 +1312,9 @@ export default function AddUpdatePolicyForm(props) {
name="conditions"
render={({ input }) => (
<PolicyConditionsComp
- policyConditionDetails={
+
policyConditionDetails={policyConditionUpdatedJSON(
serviceCompDetails.policyConditions
- }
+ )}
inputVal={input}
showModal={showModal}
handleCloseModal={
@@ -1360,11 +1343,25 @@ export default function AddUpdatePolicyForm(props) {
!isEmpty(values.conditions) ? (
Object.keys(values.conditions).map(
(keyName, keyIndex) => {
- return (
- <tr>
- <>
+ if (
+ values.conditions[keyName] != "" &&
+ values.conditions[keyName] != null
+ ) {
+ let conditionObj = find(
+
serviceCompDetails?.policyConditions,
+ function (m) {
+ if (m.name == keyName) {
+ return m;
+ }
+ }
+ );
+ return (
+ <tr key={keyName}>
<td>
- <center> {keyName} </center>
+ <center>
+ {" "}
+ {conditionObj.label}{" "}
+ </center>
</td>
<td>
{isObject(
@@ -1388,9 +1385,9 @@ export default function AddUpdatePolicyForm(props) {
</center>
)}
</td>
- </>
- </tr>
- );
+ </tr>
+ );
+ }
}
)
) : (
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyConditionsComp.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyConditionsComp.jsx
index 9498d3fe3..32d802809 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyConditionsComp.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyConditionsComp.jsx
@@ -22,7 +22,7 @@ import { Col, Form as FormB, Row, Modal, Button } from
"react-bootstrap";
import { Form, Field } from "react-final-form";
import Select from "react-select";
import CreatableSelect from "react-select/creatable";
-import { find, omit } from "lodash";
+import { find } from "lodash";
import { InfoIcon } from "../../utils/XAUtils";
import { RegexMessage } from "../../utils/XAMessages";
const esprima = require("esprima");
@@ -39,7 +39,7 @@ export default function PolicyConditionsComp(props) {
const handleSubmit = (values) => {
for (let val in values.conditions) {
if (values.conditions[val] == null || values.conditions[val] == "") {
- omit(values.conditions, val);
+ delete values.conditions[val];
}
}
inputVal.onChange(values.conditions);
@@ -126,100 +126,112 @@ export default function PolicyConditionsComp(props) {
<Modal.Body>
{policyConditionDetails?.length > 0 &&
policyConditionDetails.map((m, index) => {
- if (m.name == "accessed-after-expiry") {
- return (
- <FormB.Group className="mb-3">
- <b>{m.label}:</b>
+ let uiHintAttb =
+ m.uiHint != undefined && m.uiHint != ""
+ ? JSON.parse(m.uiHint)
+ : "";
+ if (uiHintAttb != "") {
+ if (uiHintAttb?.singleValue) {
+ return (
+ <div key={m.name}>
+ <FormB.Group className="mb-3">
+ <b>{m.label}:</b>
- <Field
- className="form-control"
- name={`conditions.${m.name}`}
- render={({ input }) => (
- <Select
- {...input}
- options={accessedOpt}
- isClearable
- value={accessedVal(input.value)}
- onChange={(val) => accessedOnChange(val,
input)}
+ <Field
+ className="form-control"
+ name={`conditions.${m.name}`}
+ render={({ input }) => (
+ <Select
+ {...input}
+ options={accessedOpt}
+ isClearable
+ value={accessedVal(input.value)}
+ onChange={(val) =>
+ accessedOnChange(val, input)
+ }
+ />
+ )}
/>
- )}
- />
- </FormB.Group>
- );
- }
- if (m.name == "expression") {
- return (
- <>
- <FormB.Group className="mb-3">
- <Row>
- <Col>
- <b>{m.label}:</b>
- <InfoIcon
- position="right"
- message={
- <p className="pd-10">
- {
- RegexMessage.MESSAGE
- .policyconditioninfoicon
- }
- </p>
- }
- />
- </Col>
- </Row>
- <Row>
- <Col>
- <Field
- name={`conditions.${m.name}`}
- validate={validater}
- render={({ input, meta }) => (
- <>
- <FormB.Control
- {...input}
- className={
- meta.error
- ? "form-control border
border-danger"
- : "form-control"
+ </FormB.Group>
+ </div>
+ );
+ }
+ if (uiHintAttb?.isMultiline) {
+ return (
+ <div key={m.name}>
+ <FormB.Group className="mb-3">
+ <Row>
+ <Col>
+ <b>{m.label}:</b>
+ <InfoIcon
+ position="right"
+ message={
+ <p className="pd-10">
+ {
+ RegexMessage.MESSAGE
+ .policyconditioninfoicon
}
- as="textarea"
- rows={3}
- />
- {meta.error && (
- <span className="invalid-field">
- {meta.error}
- </span>
- )}
- </>
- )}
- />
- </Col>
- </Row>
- </FormB.Group>
- </>
- );
- }
- if (m.name == "ip-range") {
- return (
- <FormB.Group className="mb-3">
- <b>{m.label}:</b>
+ </p>
+ }
+ />
+ </Col>
+ </Row>
+ <Row>
+ <Col>
+ <Field
+ name={`conditions.${m.name}`}
+ validate={validater}
+ render={({ input, meta }) => (
+ <>
+ <FormB.Control
+ {...input}
+ className={
+ meta.error
+ ? "form-control border
border-danger"
+ : "form-control"
+ }
+ as="textarea"
+ rows={3}
+ />
+ {meta.error && (
+ <span className="invalid-field">
+ {meta.error}
+ </span>
+ )}
+ </>
+ )}
+ />
+ </Col>
+ </Row>
+ </FormB.Group>
+ </div>
+ );
+ }
+ if (uiHintAttb?.isMultiValue) {
+ return (
+ <div key={m.name}>
+ <FormB.Group className="mb-3">
+ <b>{m.label}:</b>
- <Field
- className="form-control"
- name={`conditions.${m.name}`}
- render={({ input }) => (
- <CreatableSelect
- {...input}
- isMulti
- isClearable
- placeholder="enter expression"
- width="500px"
- value={ipRangeVal(input.value)}
- onChange={(e) => handleChange(e, input)}
+ <Field
+ className="form-control"
+ name={`conditions.${m.name}`}
+ render={({ input }) => (
+ <CreatableSelect
+ {...input}
+ isMulti
+ isClearable
+ placeholder="enter expression"
+ width="500px"
+ value={ipRangeVal(input.value)}
+ onChange={(e) => handleChange(e, input)}
+ />
+ )}
/>
- )}
- />
- </FormB.Group>
- );
+ </FormB.Group>
+ </div>
+ );
+ }
}
})}
</Modal.Body>
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyPermissionItem.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyPermissionItem.jsx
index af1191f99..821d39370 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyPermissionItem.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/PolicyPermissionItem.jsx
@@ -28,7 +28,13 @@ import { toast } from "react-toastify";
import Editable from "Components/Editable";
import { RangerPolicyType } from "Utils/XAEnums";
import TagBasePermissionItem from "./TagBasePermissionItem";
-import { dragStart, dragEnter, drop, dragOver } from "../../utils/XAUtils";
+import {
+ dragStart,
+ dragEnter,
+ drop,
+ dragOver,
+ policyConditionUpdatedJSON
+} from "../../utils/XAUtils";
const noneOptions = {
label: "None",
@@ -171,13 +177,14 @@ export default function PolicyPermissionItem(props) {
const required = (value) => (value ? undefined : "Required");
- const requiredForPermission = (fieldVals, index) => {
+ const requiredForPolicyItem = (fieldVals, index) => {
if (fieldVals && !isEmpty(fieldVals[index])) {
let error, accTypes;
let users = (fieldVals[index]?.users || []).length > 0;
let grps = (fieldVals[index]?.groups || []).length > 0;
let roles = (fieldVals[index]?.roles || []).length > 0;
let delegateAdmin = fieldVals[index]?.delegateAdmin;
+ let policyConditionVal = fieldVals[index]?.conditions;
if (fieldVals[index]?.accesses && !isArray(fieldVals[index]?.accesses)) {
if (serviceCompDetails?.name == "tag") {
accTypes =
@@ -208,26 +215,29 @@ export default function PolicyPermissionItem(props) {
"Please select users/groups/roles for selected permission item";
}
}
- return error;
- }
- };
-
- const requiredForDeleGateAdmin = (fieldVals, index) => {
- if (
- !isEmpty(fieldVals?.[index]) &&
- has(fieldVals?.[index], "delegateAdmin")
- ) {
- let delError;
- let users = (fieldVals[index]?.users || []).length > 0;
- let grps = (fieldVals[index]?.groups || []).length > 0;
- let roles = (fieldVals[index]?.roles || []).length > 0;
- let delegateAdmin = fieldVals[index]?.delegateAdmin;
-
if (delegateAdmin && !users && !grps && !roles) {
- delError =
- "Please select user/group/role for the selected delegate Admin";
+ error = "Please select user/group/role for the selected delegate
Admin";
}
- return delError;
+ if (policyConditionVal) {
+ for (const key in policyConditionVal) {
+ if (
+ policyConditionVal[key] == null ||
+ policyConditionVal[key] == ""
+ ) {
+ delete policyConditionVal[key];
+ }
+ }
+ if (
+ Object.keys(policyConditionVal).length != 0 &&
+ !users &&
+ !grps &&
+ !roles
+ ) {
+ error =
+ "Please select user/group/role for the entered policy condition";
+ }
+ }
+ return error;
}
};
@@ -386,47 +396,35 @@ export default function PolicyPermissionItem(props) {
);
}
if (colName == "Policy Conditions") {
- return serviceCompDetails?.policyConditions?.length
==
- 1 ? (
- <td key={colName} className="align-middle">
- <Field
- className="form-control"
- name={`${name}.conditions`}
- render={({ input, meta }) => (
- <div className="table-editable">
- <Editable
- {...input}
- placement="auto"
- type="select"
- conditionDefVal={
- serviceCompDetails.policyConditions[0]
- }
- servicedefName={serviceCompDetails.name}
- selectProps={{ isMulti: true }}
- />
- </div>
- )}
- />
- </td>
- ) : (
- <td key={colName} className="align-middle">
- <Field
- className="form-control"
- name={`${name}.conditions`}
- render={({ input, meta }) => (
- <div className="table-editable">
- <Editable
- {...input}
- placement="auto"
- type="custom"
- conditionDefVal={
- serviceCompDetails.policyConditions
- }
- />
- </div>
- )}
- />
- </td>
+ return (
+ serviceCompDetails?.policyConditions?.length >
+ 0 && (
+ <td key={colName} className="align-middle">
+ <Field
+ className="form-control"
+ name={`${name}.conditions`}
+ validate={(value, formValues) =>
+ requiredForPolicyItem(
+ formValues[attrName],
+ index
+ )
+ }
+ render={({ input, meta }) => (
+ <div className="table-editable">
+ <Editable
+ {...input}
+ placement="auto"
+ type="custom"
+
conditionDefVal={policyConditionUpdatedJSON(
+ serviceCompDetails.policyConditions
+ )}
+ selectProps={{ isMulti: true }}
+ />
+ </div>
+ )}
+ />
+ </td>
+ )
);
}
if (colName == "Permissions") {
@@ -437,7 +435,7 @@ export default function PolicyPermissionItem(props) {
className="form-control"
name={`${name}.accesses`}
validate={(value, formValues) =>
- requiredForPermission(
+ requiredForPolicyItem(
formValues[attrName],
index
)
@@ -463,7 +461,7 @@ export default function PolicyPermissionItem(props) {
className="form-control"
name={`${name}.accesses`}
validate={(value, formValues) =>
- requiredForPermission(
+ requiredForPolicyItem(
formValues[attrName],
index
)
@@ -646,7 +644,7 @@ export default function PolicyPermissionItem(props) {
className="form-control"
name={`${name}.delegateAdmin`}
validate={(value, formValues) =>
- requiredForDeleGateAdmin(
+ requiredForPolicyItem(
formValues[attrName],
index
)
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
index f75ddd4c3..a4ccffb5e 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
@@ -277,7 +277,8 @@ export default function ServiceAuditFilter(props) {
<Select
{...input}
menuPortalTarget={document.body}
- isClearable={false}
+ isClearable={true}
+ isSearchable={false}
options={[
{ value: "DENIED", label: "DENIED" },
{ value: "ALLOWED", label: "ALLOWED" },
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
index 83b2b386b..633a934ee 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
@@ -191,7 +191,8 @@ class ServiceForm extends Component {
if (values?.customConfigs !== undefined) {
values.customConfigs?.map((config) => {
- config !== undefined &&
+ config?.name !== undefined &&
+ config?.value !== undefined &&
(serviceJson["configs"][config.name] = config.value);
});
}