This is an automated email from the ASF dual-hosted git repository. madhan 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 19dbc93e5 RANGER-4280 : Update security-zone UI with addition of admin-roles and audit-roles 19dbc93e5 is described below commit 19dbc93e5a74d3682bf6eda691344048cad2b8ca Author: Dhaval.Rajpara <dhavalrajpara1...@gmail.com> AuthorDate: Fri Oct 20 15:18:54 2023 +0530 RANGER-4280 : Update security-zone UI with addition of admin-roles and audit-roles Signed-off-by: Madhan Neethiraj <mad...@apache.org> --- .../service/RangerSecurityZoneServiceService.java | 2 + .../src/views/SecurityZone/SecurityZoneForm.jsx | 268 +++++++++++++++++++-- .../src/views/SecurityZone/ZoneDisplay.jsx | 46 ++++ 3 files changed, 293 insertions(+), 23 deletions(-) diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerSecurityZoneServiceService.java b/security-admin/src/main/java/org/apache/ranger/service/RangerSecurityZoneServiceService.java index 8acdd9813..ae6833815 100644 --- a/security-admin/src/main/java/org/apache/ranger/service/RangerSecurityZoneServiceService.java +++ b/security-admin/src/main/java/org/apache/ranger/service/RangerSecurityZoneServiceService.java @@ -82,6 +82,8 @@ public class RangerSecurityZoneServiceService extends RangerSecurityZoneServiceB trxLogAttrs.put("adminUserGroups", new VTrxLogAttr("adminUserGroups", "Zone Admin User Groups", false)); trxLogAttrs.put("auditUsers", new VTrxLogAttr("auditUsers", "Zone Audit Users", false)); trxLogAttrs.put("auditUserGroups", new VTrxLogAttr("auditUserGroups", "Zone Audit User Groups", false)); + trxLogAttrs.put("adminRoles", new VTrxLogAttr("adminRoles", "Zone Admin Roles", false)); + trxLogAttrs.put("auditRoles", new VTrxLogAttr("auditRoles", "Zone Audit Roles", false)); trxLogAttrs.put("description", new VTrxLogAttr("description", "Zone Description", false)); trxLogAttrs.put("tagServices", new VTrxLogAttr("tagServices", "Zone Tag Services", false)); } diff --git a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx index 3bf0df71c..91d2a3758 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx @@ -79,6 +79,14 @@ const SecurityZoneForm = () => { }); const [preventUnBlock, setPreventUnblock] = useState(false); const [blockUI, setBlockUI] = useState(false); + const [userLoading, setUserLoading] = useState(false); + const [groupLoading, setGroupLoading] = useState(false); + const [roleLoading, setRoleLoading] = useState(false); + const [tagServiceLoading, setTagServiceLoading] = useState(false); + const [defaultUserOptions, setDefaultUserOptions] = useState([]); + const [defaultGroupOptions, setDefaultGroupOptions] = useState([]); + const [defaultRoleOptions, setDefaultRoleOptions] = useState([]); + const [defaultTagServiceOptions, setDefaultTagServiceOptions] = useState([]); useEffect(() => { fetchInitalData(); @@ -103,10 +111,18 @@ const SecurityZoneForm = () => { } } - if (isEmpty(values.adminUsers) && isEmpty(values.adminUserGroups)) { + if ( + isEmpty(values?.adminUsers) && + isEmpty(values?.adminUserGroups) && + isEmpty(values?.adminRoles) + ) { + errors.adminRoles = { + required: true, + text: "Please provide atleast one admin user or group or role !" + }; errors.adminUserGroups = { required: true, - text: "Please provide atleast one admin user or group!" + text: "" }; errors.adminUsers = { required: true, @@ -114,10 +130,18 @@ const SecurityZoneForm = () => { }; } - if (isEmpty(values.auditUsers) && isEmpty(values.auditUserGroups)) { + if ( + isEmpty(values?.auditUsers) && + isEmpty(values?.auditUserGroups) && + isEmpty(values?.auditRoles) + ) { + errors.auditRoles = { + required: true, + text: "Please provide atleast one audit user or group or role !" + }; errors.auditUserGroups = { required: true, - text: "Please provide atleast one audit user or group!" + text: "" }; errors.auditUsers = { required: true, @@ -308,6 +332,20 @@ const SecurityZoneForm = () => { } } + if (values.adminRoles) { + zoneData.adminRoles = []; + for (let key of Object.keys(values.adminRoles)) { + zoneData.adminRoles.push(values.adminRoles[key].label || ""); + } + } + + if (values.auditRoles) { + zoneData.auditRoles = []; + for (let key of Object.keys(values.auditRoles)) { + zoneData.auditRoles.push(values.auditRoles[key].label || ""); + } + } + zoneData.tagServices = []; if (values.tagServices) { @@ -404,6 +442,20 @@ const SecurityZoneForm = () => { ); } + zoneData.adminRoles = []; + if (zone.adminRoles) { + zone.adminRoles.map((name) => + zoneData.adminRoles.push({ label: name, value: name }) + ); + } + + zoneData.auditRoles = []; + if (zone.auditRoles) { + zone.auditRoles.map((name) => + zoneData.auditRoles.push({ label: name, value: name }) + ); + } + zoneData.tagServices = []; if (zone.tagServices) { zone.tagServices.map((name) => @@ -474,7 +526,7 @@ const SecurityZoneForm = () => { return zoneData; }; - const fetchUsers = async (inputValue) => { + const fetchUsersData = async (inputValue) => { let params = { isVisible: 1 }; let usersOp = []; @@ -489,7 +541,7 @@ const SecurityZoneForm = () => { }); usersOp = userResp.data?.vXStrings; } catch (error) { - console.error(`Error occurred while fetching Users ! ${error}`); + console.error(`Error occurred while fetching Users! ${error}`); serverError(error); } @@ -498,7 +550,7 @@ const SecurityZoneForm = () => { }); }; - const fetchGroups = async (inputValue) => { + const fetchGroupsData = async (inputValue) => { let params = { isVisible: 1 }; let groupsOp = []; @@ -513,7 +565,7 @@ const SecurityZoneForm = () => { }); groupsOp = groupResp.data?.vXStrings; } catch (error) { - console.error(`Error occurred while fetching Groups ! ${error}`); + console.error(`Error occurred while fetching Groups! ${error}`); serverError(error); } @@ -522,6 +574,26 @@ const SecurityZoneForm = () => { }); }; + const fetchRolesData = async (inputValue) => { + let params = { roleNamePartial: inputValue || "" }; + let op = []; + + try { + const roleResp = await fetchApi({ + url: "roles/roles", + params: params + }); + op = roleResp.data.roles; + } catch (error) { + console.error(`Error occurred while fetching Roles! ${error}`); + serverError(error); + } + return op.map((obj) => ({ + label: obj.name, + value: obj.name + })); + }; + const fetchTagServices = async (inputValue) => { let params = {}; if (inputValue) { @@ -631,7 +703,35 @@ const SecurityZoneForm = () => { </p> )); }; + const onFocusUserSelect = () => { + setUserLoading(true); + fetchUsersData().then((opts) => { + setDefaultUserOptions(opts); + setUserLoading(false); + }); + }; + const onFocusGroupSelect = () => { + setGroupLoading(true); + fetchGroupsData().then((opts) => { + setDefaultGroupOptions(opts); + setGroupLoading(false); + }); + }; + const onFocusRoleSelect = () => { + setRoleLoading(true); + fetchRolesData().then((opts) => { + setDefaultRoleOptions(opts); + setRoleLoading(false); + }); + }; + const onFocusTagServiceSelect = () => { + setTagServiceLoading(true); + fetchTagServices().then((opts) => { + setDefaultTagServiceOptions(opts); + setTagServiceLoading(false); + }); + }; return ( <React.Fragment> <div className="clearfix"> @@ -757,14 +857,20 @@ const SecurityZoneForm = () => { : "auditUsers" } cacheOptions - defaultOptions - loadOptions={fetchUsers} + loadOptions={fetchUsersData} + onFocus={() => { + onFocusUserSelect(); + }} + defaultOptions={defaultUserOptions} + noOptionsMessage={() => + userLoading ? "Loading..." : "No options" + } isMulti components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }} - isClearable={false} + isClearable={true} placeholder="Select User" /> </Col> @@ -794,16 +900,66 @@ const SecurityZoneForm = () => { : "adminUserGroups" } {...input} - defaultOptions - loadOptions={fetchGroups} + cacheOptions + loadOptions={fetchGroupsData} + onFocus={() => { + onFocusGroupSelect(); + }} + defaultOptions={defaultGroupOptions} + noOptionsMessage={() => + groupLoading ? "Loading..." : "No options" + } isMulti components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }} - isClearable={false} + isClearable={true} placeholder="Select Group" - required + /> + </Col> + </Row> + )} + /> + + <Field + name="adminRoles" + render={({ input, meta }) => ( + <Row className="form-group"> + <Col xs={3}> + <label className="form-label pull-right"> + Admin Roles + </label> + </Col> + <Col xs={4}> + <AsyncSelect + {...input} + styles={ + meta.error && meta.touched + ? selectCustomStyles + : "" + } + id={ + meta.error && meta.touched + ? "isError" + : "adminRoles" + } + cacheOptions + loadOptions={fetchRolesData} + onFocus={() => { + onFocusRoleSelect(); + }} + defaultOptions={defaultRoleOptions} + noOptionsMessage={() => + roleLoading ? "Loading..." : "No options" + } + isMulti + components={{ + DropdownIndicator: () => null, + IndicatorSeparator: () => null + }} + isClearable={true} + placeholder="Select Role" /> {meta.touched && meta.error && ( <span className="invalid-field"> @@ -837,14 +993,21 @@ const SecurityZoneForm = () => { ? "isError" : "auditUsers" } - defaultOptions - loadOptions={fetchUsers} + cacheOptions + loadOptions={fetchUsersData} + onFocus={() => { + onFocusUserSelect(); + }} + defaultOptions={defaultUserOptions} + noOptionsMessage={() => + userLoading ? "Loading..." : "No options" + } isMulti components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }} - isClearable={false} + isClearable={true} placeholder="Select User" /> </Col> @@ -873,16 +1036,67 @@ const SecurityZoneForm = () => { ? "isError" : "auditUserGroups" } - defaultOptions - loadOptions={fetchGroups} + cacheOptions + loadOptions={fetchGroupsData} + onFocus={() => { + onFocusGroupSelect(); + }} + defaultOptions={defaultGroupOptions} + noOptionsMessage={() => + groupLoading ? "Loading..." : "No options" + } isMulti components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }} - isClearable={false} + isClearable={true} placeholder="Select Group" /> + </Col> + </Row> + )} + /> + + <Field + name="auditRoles" + render={({ input, meta }) => ( + <Row className="form-group"> + <Col xs={3}> + <label className="form-label pull-right"> + Auditor Roles + </label> + </Col> + <Col xs={4}> + <AsyncSelect + {...input} + styles={ + meta.error && meta.touched + ? selectCustomStyles + : "" + } + id={ + meta.error && meta.touched + ? "isError" + : "auditRoles" + } + cacheOptions + loadOptions={fetchRolesData} + onFocus={() => { + onFocusRoleSelect(); + }} + defaultOptions={defaultRoleOptions} + noOptionsMessage={() => + roleLoading ? "Loading..." : "No options" + } + isMulti + components={{ + DropdownIndicator: () => null, + IndicatorSeparator: () => null + }} + isClearable={true} + placeholder="Select Role" + /> {meta.error && meta.touched && ( <span className="invalid-field"> {meta.error.text} @@ -892,6 +1106,7 @@ const SecurityZoneForm = () => { </Row> )} /> + <p className="form-header">Services:</p> <Field name="tagServices" @@ -905,14 +1120,21 @@ const SecurityZoneForm = () => { <Col xs={6}> <AsyncSelect {...input} - defaultOptions + cacheOptions loadOptions={fetchTagServices} + onFocus={() => { + onFocusTagServiceSelect(); + }} + defaultOptions={defaultTagServiceOptions} + noOptionsMessage={() => + tagServiceLoading ? "Loading..." : "No options" + } isMulti components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }} - isClearable={false} + isClearable={true} placeholder="Select Tag Services" /> </Col> diff --git a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/ZoneDisplay.jsx b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/ZoneDisplay.jsx index 5a8cbe540..61bc250e8 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/ZoneDisplay.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/ZoneDisplay.jsx @@ -227,6 +227,29 @@ class ZoneDisplay extends Component { )} </Col> </Form.Group> + <Form.Group as={Row} className="mb-3"> + <Form.Label className="text-right" column sm="3"> + Admin Roles + </Form.Label> + <Col sm="9" className="pt-2"> + {this.props?.zone?.adminRoles?.length > 0 ? ( + this.props?.zone.adminRoles?.map((obj) => { + return ( + <Badge + variant="primary" + className="mr-1 more-less-width text-truncate" + key={obj} + title={obj} + > + {obj} + </Badge> + ); + }) + ) : ( + <p className="mt-1">--</p> + )} + </Col> + </Form.Group> <Form.Group as={Row} className="mb-3"> <Form.Label className="text-right" column sm="3"> Auditor Users @@ -273,6 +296,29 @@ class ZoneDisplay extends Component { )} </Col> </Form.Group> + <Form.Group as={Row} className="mb-3"> + <Form.Label className="text-right" column sm="3"> + Auditor Roles + </Form.Label> + <Col sm="9" className="pt-2"> + {this.props?.zone?.auditRoles?.length > 0 ? ( + this.props?.zone?.auditRoles?.map((obj) => { + return ( + <Badge + variant="primary" + className="mr-1 more-less-width text-truncate" + key={obj} + title={obj} + > + {obj} + </Badge> + ); + }) + ) : ( + <span className="mt-1">--</span> + )} + </Col> + </Form.Group> </Form> </Card.Body> </Accordion.Collapse>