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
commit 70770ef6e4ccf406f6f8ca5e3778b013af6e0a62 Author: Brijesh Bhalala <[email protected]> AuthorDate: Thu Jan 11 18:59:30 2024 +0530 RANGER-4641: Handling empty values while displaying ActivationTime & implementing Column Hide/Show functionality in Audit Plugin Status Module Signed-off-by: Madhan Neethiraj <[email protected]> --- .../src/components/CommonComponents.jsx | 6 +- .../react-webapp/src/components/XATableLayout.jsx | 111 +++++-- .../main/webapp/react-webapp/src/styles/style.css | 10 + .../main/webapp/react-webapp/src/utils/XAEnums.js | 23 ++ .../webapp/react-webapp/src/utils/XAMessages.js | 22 ++ .../main/webapp/react-webapp/src/utils/XAUtils.js | 4 +- .../src/views/AuditEvent/PluginStatusLogs.jsx | 349 ++++++++------------- .../src/views/AuditEvent/PluginsLog.jsx | 10 +- 8 files changed, 283 insertions(+), 252 deletions(-) diff --git a/security-admin/src/main/webapp/react-webapp/src/components/CommonComponents.jsx b/security-admin/src/main/webapp/react-webapp/src/components/CommonComponents.jsx index 1fca37866..79fcbd11a 100644 --- a/security-admin/src/main/webapp/react-webapp/src/components/CommonComponents.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/components/CommonComponents.jsx @@ -441,7 +441,11 @@ export const CustomPopoverTagOnClick = ({ export const CustomTooltip = ({ placement, content, icon }) => ( <OverlayTrigger placement={placement} - overlay={<Popover id={`tooltip-${placement}`}>{content}</Popover>} + overlay={ + <Popover id={`tooltip-${placement}`}> + <span className=" d-block px-2 py-1">{content}</span> + </Popover> + } > <i className={icon} data-id="infoTextFiled" data-cy="infoTextFiled"></i> </OverlayTrigger> diff --git a/security-admin/src/main/webapp/react-webapp/src/components/XATableLayout.jsx b/security-admin/src/main/webapp/react-webapp/src/components/XATableLayout.jsx index 71a33f1c7..d75315e04 100644 --- a/security-admin/src/main/webapp/react-webapp/src/components/XATableLayout.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/components/XATableLayout.jsx @@ -28,7 +28,7 @@ import { } from "react-table"; import { Table, ButtonGroup } from "react-bootstrap"; import DropdownButton from "react-bootstrap/DropdownButton"; -import { isEmpty } from "lodash"; +import { groupBy, isEmpty, uniq, uniqBy } from "lodash"; const IndeterminateCheckbox = forwardRef( ({ indeterminate, chkType, ...rest }, ref) => { @@ -195,6 +195,22 @@ function XATableLayout({ </span> ); }; + /* For Column Visibility */ + const groupedColumnsData = groupBy(allColumns, "parent[id]"); + delete groupedColumnsData.undefined; + const allColumnsData = allColumns.map((column) => { + const columnName = column?.parent + ? `Group(${column?.parent.id})` + : `Non-Group(${column?.id})`; + return { + columnName, + columnData: groupedColumnsData[column.parent?.id] || column + }; + }); + + let filterAllColumns = uniqBy(allColumnsData, function (column) { + return column.columnName; + }); let columnShowHide = []; return ( @@ -203,7 +219,7 @@ function XATableLayout({ {columnHide && ( <div className="text-right mb-2 mt-n5"> <DropdownButton - className="p-0" + className="p-0 column-dropdown" menuAlign="right" as={ButtonGroup} size="sm" @@ -211,35 +227,70 @@ function XATableLayout({ variant="info" title="Columns" > - <ul className="list-group"> - {allColumns.map((column, index) => { - columnShowHide.push({ - name: column.id, - renderable: column.isVisible - }); - - localStorage.setItem( - "showHideTableCol", - JSON.stringify({ bigData: columnShowHide }) - ); - return ( - <li - className="column-list text-truncate" - key={`col-${index}`} - > - <label> - <input - className="mr-1" - type="checkbox" - {...column.getToggleHiddenProps()} - /> + <div className="column-dropdown-maxheight"> + <ul className="list-group fnt-14"> + {filterAllColumns.map((column, index) => { + columnShowHide.push({ + name: column.columnData.id, + renderable: column.columnData.isVisible + }); - {column.Header} - </label> - </li> - ); - })} - </ul> + localStorage.setItem( + "showHideTableCol", + JSON.stringify({ bigData: columnShowHide }) + ); + return column.columnName == + `Non-Group(${column.columnData.id})` ? ( + <li + className="column-list text-truncate" + key={`col-${index}`} + > + <label + title={column.columnData.Header} + className="d-flex align-items-center" + > + <input + className="mr-1" + type="checkbox" + {...column.columnData.getToggleHiddenProps()} + /> + {column.columnData.Header} + </label> + </li> + ) : ( + <li + className="column-list text-truncate" + key={`col-${index}`} + > + {column.columnName !== undefined && ( + <div className="font-weight-bold text-secondary mb-2 fnt-14"> + {column.columnName !== undefined && + column?.columnData[0]?.parent?.id} + </div> + )} + {column.columnData.map((columns) => ( + <ul key={columns.id} className="p-0"> + <li className=" list-unstyled"> + {" "} + <label + title={columns.Header} + className="d-flex align-items-center" + > + <input + className="mr-1" + type="checkbox" + {...columns.getToggleHiddenProps()} + /> + {columns.Header} + </label> + </li> + </ul> + ))} + </li> + ); + })} + </ul> + </div> </DropdownButton> </div> )} diff --git a/security-admin/src/main/webapp/react-webapp/src/styles/style.css b/security-admin/src/main/webapp/react-webapp/src/styles/style.css index ed45e8636..f5caff0bb 100755 --- a/security-admin/src/main/webapp/react-webapp/src/styles/style.css +++ b/security-admin/src/main/webapp/react-webapp/src/styles/style.css @@ -3032,3 +3032,13 @@ textarea.gds-placeholder::placeholder, max-height: 126px; overflow-y: scroll; } + +.column-dropdown .dropdown-toggle:focus { + color: #fff; + background-color: #117a8b; + border-color: none; +} +.column-dropdown-maxheight { + max-height: 400px; + overflow-y: auto; +} diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js index 842cb690e..012463e80 100755 --- a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js +++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js @@ -893,3 +893,26 @@ export const UsersyncDetailsKeyDisplayMap = { groupSearchFirstEnabled: "Group search first enabled", groupHierarchyLevel: "Group hierarchy level" }; + +export const pluginStatusColumnInfoMsg = { + Policy: { + title: "Policy (Time details)", + lastUpdated: "Last updated time of policy.", + downloadTime: "Time when policy actually downloaded(sync-up with Ranger).", + activeTime: "Time when policy actually in use for enforcement.", + downloadTimeDelayMsg: + "Policy is updated but not yet downloaded(sync-up with Ranger).", + activationTimeDelayMsg: + "Policy is updated but not yet used for any enforcement." + }, + Tag: { + title: "Tag Policy (Time details)", + lastUpdated: "Last updated time of Tag-service.", + downloadTime: "Time when tag-based policies sync-up with Ranger.", + activeTime: "Time when tag-based policies in use for enforcement.", + downloadTimeDelayMsg: + "Policy is updated but not yet downloaded(sync-up with Ranger).", + activationTimeDelayMsg: + "Policy is updated but not yet used for any enforcement." + } +}; diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAMessages.js b/security-admin/src/main/webapp/react-webapp/src/utils/XAMessages.js index d22ecfa7c..ee7b010ea 100644 --- a/security-admin/src/main/webapp/react-webapp/src/utils/XAMessages.js +++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAMessages.js @@ -18,6 +18,7 @@ */ import React from "react"; +import { pluginStatusColumnInfoMsg } from "./XAEnums"; export const RegexMessage = { MESSAGE: { @@ -129,3 +130,24 @@ export const udfResourceWarning = () => { </p> ); }; + +/* PluginStatus Column Info */ + +export const pluginStatusColumnInfo = (colName) => { + return ( + <ul className="list-inline"> + <li className="list-inline-item"> + <strong>Last Update: </strong>{" "} + {pluginStatusColumnInfoMsg[colName].lastUpdated} + </li> + <li className="list-inline-item"> + <strong>Download: </strong> + {pluginStatusColumnInfoMsg[colName].downloadTime} + </li> + <li className="list-inline-item"> + <strong>Active: </strong>{" "} + {pluginStatusColumnInfoMsg[colName].activeTime} + </li> + </ul> + ); +}; 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 f834f9795..d189e4aeb 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 @@ -198,7 +198,9 @@ export const setTimeStamp = (dateTime) => { <span title={formatDateTime}> {formatDateTime} <div className="text-muted"> - <small>{moment(formatDateTime).fromNow()}</small> + <small> + {moment(formatDateTime, "MM/DD/YYYY h:mm:ss A").fromNow()} + </small> </div> </span> ) : ( diff --git a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginStatusLogs.jsx b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginStatusLogs.jsx index 07ac391d9..638078298 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginStatusLogs.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginStatusLogs.jsx @@ -23,7 +23,6 @@ import { Row, Col } from "react-bootstrap"; import XATableLayout from "Components/XATableLayout"; import { AuditFilterEntries } from "Components/CommonComponents"; import moment from "moment-timezone"; -import dateFormat from "dateformat"; import { setTimeStamp, fetchSearchFilterParams, @@ -39,8 +38,18 @@ import { } from "../../components/CommonComponents"; import StructuredFilter from "../../components/structured-filter/react-typeahead/tokenizer"; import { fetchApi } from "Utils/fetchAPI"; -import { isUndefined, map, sortBy, toUpper, filter } from "lodash"; +import { + isUndefined, + map, + sortBy, + toUpper, + filter, + isNull, + isEmpty +} from "lodash"; import { getServiceDef } from "../../utils/appState"; +import { pluginStatusColumnInfo } from "../../utils/XAMessages"; +import { pluginStatusColumnInfoMsg } from "../../utils/XAEnums"; function Plugin_Status() { const context = useOutletContext(); @@ -120,49 +129,111 @@ function Plugin_Status() { return diff < 0 ? true : false; }; + const getLastUpdateTime = (lastUpdateTime) => { + if (isUndefined(lastUpdateTime) || isNull(lastUpdateTime)) { + return <center>--</center>; + } + return setTimeStamp(lastUpdateTime); + }; + + const getDownloadTime = (downloadTime, lastUpdateTime, columnsName) => { + if (isUndefined(downloadTime) || isNull(downloadTime)) { + return <center>--</center>; + } + + if (!isUndefined(lastUpdateTime)) { + let downloadDate = new Date(parseInt(downloadTime)); + let lastUpdateDate = new Date(parseInt(lastUpdateTime)); + if (isDateDifferenceMoreThanHr(downloadDate, lastUpdateDate)) { + if ( + moment(downloadDate).diff(moment(lastUpdateDate), "minutes") >= -2 + ) { + return ( + <span className="text-warning"> + <CustomTooltip + placement="bottom" + content={ + pluginStatusColumnInfoMsg[columnsName].downloadTimeDelayMsg + } + icon="fa-fw fa fa-exclamation-circle active-policy-alert" + /> + {setTimeStamp(downloadTime)} + </span> + ); + } else { + return ( + <span className="text-error"> + <CustomTooltip + placement="bottom" + content={ + pluginStatusColumnInfoMsg[columnsName].downloadTimeDelayMsg + } + icon="fa-fw fa fa-exclamation-circle active-policy-alert" + /> + {setTimeStamp(downloadTime)}{" "} + </span> + ); + } + } + } + return setTimeStamp(downloadTime); + }; + + const getActivationTime = (activeTime, lastUdpateTime, columnsName) => { + if (isUndefined(activeTime) || isNull(activeTime) || activeTime == 0) { + return <center>--</center>; + } + if (!isUndefined(lastUdpateTime)) { + let activeDate = new Date(parseInt(activeTime)); + let lastUpdateDate = new Date(parseInt(lastUdpateTime)); + if (isDateDifferenceMoreThanHr(activeDate, lastUpdateDate)) { + if (moment(activeDate).diff(moment(lastUpdateDate), "minutes") >= -2) { + return ( + <span className="text-warning"> + <CustomTooltip + placement="bottom" + content={ + pluginStatusColumnInfoMsg[columnsName].activationTimeDelayMsg + } + icon="fa-fw fa fa-exclamation-circle active-policy-alert" + /> + {setTimeStamp(activeTime)} + </span> + ); + } else { + return ( + <span className="text-error"> + <CustomTooltip + placement="bottom" + content={ + pluginStatusColumnInfoMsg[columnsName].activationTimeDelayMsg + } + icon="fa-fw fa fa-exclamation-circle active-policy-alert" + /> + {setTimeStamp(activeTime)} + </span> + ); + } + } + } + return setTimeStamp(activeTime); + }; + const refreshTable = () => { setPluginStatusLogs([]); setLoader(true); setUpdateTable(moment.now()); }; - const contents = (val) => { - return ( - <> - <ul className="list-inline"> - <li className="list-inline-item"> - <strong>Last Update: </strong> Last updated time of{" "} - {val == "Tag" ? "Tag-service" : "policy"}. - </li> - <li className="list-inline-item"> - <strong>Download: </strong> - {val == "Tag" - ? "Time when tag-based policies sync-up with Ranger." - : "Time when policy actually downloaded(sync-up with Ranger)."} - </li> - <li className="list-inline-item"> - <strong>Active: </strong> Time when{" "} - {val == "Tag" ? "tag-based" : "policy"} actually in use for - enforcement. - </li> - </ul> - </> - ); - }; - - const infoIcon = (val) => { + const infoIcon = (columnsName) => { return ( <> - <b>{val} ( Time )</b> + <b>{columnsName} ( Time )</b> <CustomPopover icon="fa-fw fa fa-info-circle info-icon" - title={ - val == "Policy" - ? "Policy (Time details)" - : "Tag Policy (Time details)" - } - content={contents(val)} + title={pluginStatusColumnInfoMsg[columnsName].title} + content={pluginStatusColumnInfo(columnsName)} placement="left" trigger={["hover", "focus"]} /> @@ -217,18 +288,18 @@ function Plugin_Status() { accessor: "clusterName", Cell: ({ row: { original } }) => { let clusterName = original?.info?.clusterName; - return isUndefined(clusterName) ? "--" : clusterName; + return !isEmpty(clusterName) ? clusterName : <center>--</center>; } }, { Header: infoIcon("Policy"), - id: "policyinfo", + id: "Policy (Time)", columns: [ { Header: "Last Update", accessor: "lastPolicyUpdateTime", Cell: ({ row: { original } }) => { - return setTimeStamp(original.info.lastPolicyUpdateTime); + return getLastUpdateTime(original.info.lastPolicyUpdateTime); }, minWidth: 190 }, @@ -236,54 +307,11 @@ function Plugin_Status() { Header: "Download", accessor: "policyDownloadTime", Cell: ({ row: { original } }) => { - var downloadDate = new Date( - parseInt(original.info.policyDownloadTime) - ); - - dateFormat( - parseInt(original.info.policyDownloadTime), - "mm/dd/yyyy hh:MM:ss TT" + return getDownloadTime( + original.info.policyDownloadTime, + original.info.lastPolicyUpdateTime, + "Policy" ); - if (!isUndefined(original.info.lastPolicyUpdateTime)) { - var lastUpdateDate = new Date( - parseInt(original.info.lastPolicyUpdateTime) - ); - if (isDateDifferenceMoreThanHr(downloadDate, lastUpdateDate)) { - if ( - moment(downloadDate).diff( - moment(lastUpdateDate), - "minutes" - ) >= -2 - ) { - return ( - <span className="text-warning"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet downloaded(sync-upwith Ranger)" - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.policyDownloadTime)} - </span> - ); - } else { - return ( - <span className="text-error"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet downloaded(sync-upwith Ranger)" - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.policyDownloadTime)}{" "} - </span> - ); - } - } - } - return setTimeStamp(original.info.policyDownloadTime); }, minWidth: 190 }, @@ -291,51 +319,11 @@ function Plugin_Status() { Header: "Active", accessor: "policyActivationTime", Cell: ({ row: { original } }) => { - let activeDate = new Date( - parseInt(original.info.policyActivationTime) + return getActivationTime( + original.info.policyActivationTime, + original.info.lastPolicyUpdateTime, + "Policy" ); - - if (!isUndefined(original.info.lastPolicyUpdateTime)) { - let lastUpdateDate = new Date( - parseInt(original.info.lastPolicyUpdateTime) - ); - - if (isDateDifferenceMoreThanHr(activeDate, lastUpdateDate)) { - if ( - moment(activeDate).diff( - moment(lastUpdateDate), - "minutes" - ) >= -2 - ) { - return ( - <span className="text-warning"> - <CustomTooltip - placement="bottom" - content={ - " Policy is updated but not yet used for any enforcement." - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.policyActivationTime)} - </span> - ); - } else { - return ( - <span className="text-error"> - <CustomTooltip - placement="bottom" - content={ - " Policy is updated but not yet used for any enforcement." - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.policyActivationTime)} - </span> - ); - } - } - } - return setTimeStamp(original.info.policyActivationTime); }, minWidth: 190 } @@ -343,13 +331,13 @@ function Plugin_Status() { }, { Header: infoIcon("Tag"), - id: "taginfo", + id: "Tag (Time)", columns: [ { Header: "Last Update", accessor: "lastTagUpdateTime", Cell: ({ row: { original } }) => { - return setTimeStamp(original.info.lastTagUpdateTime); + return getLastUpdateTime(original.info.lastTagUpdateTime); }, minWidth: 190 }, @@ -358,50 +346,11 @@ function Plugin_Status() { accessor: "tagDownloadTime", Cell: ({ row: { original } }) => { - var downloadDate = new Date( - parseInt(original.info.tagDownloadTime) + return getDownloadTime( + original.info.tagDownloadTime, + original.info.lastTagUpdateTime, + "Tag" ); - - if (!isUndefined(original.info.lastTagUpdateTime)) { - var lastUpdateDate = new Date( - parseInt(original.info.lastTagUpdateTime) - ); - if (isDateDifferenceMoreThanHr(downloadDate, lastUpdateDate)) { - if ( - moment(downloadDate).diff( - moment(lastUpdateDate), - "minutes" - ) >= -2 - ) { - return ( - <span className="text-warning"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet downloaded(sync-upwith Ranger)" - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.tagDownloadTime)} - </span> - ); - } else { - return ( - <span className="text-error"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet downloaded(sync-upwith Ranger)" - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.tagDownloadTime)}{" "} - </span> - ); - } - } - } - return setTimeStamp(original.info.tagDownloadTime); }, minWidth: 190 }, @@ -410,50 +359,11 @@ function Plugin_Status() { accessor: "tagActivationTime", Cell: ({ row: { original } }) => { - var downloadDate = new Date( - parseInt(original.info.tagActivationTime) + return getActivationTime( + original.info.tagActivationTime, + original.info.lastTagUpdateTime, + "Tag" ); - - if (!isUndefined(original.info.lastTagUpdateTime)) { - var lastUpdateDate = new Date( - parseInt(original.info.lastTagUpdateTime) - ); - if (isDateDifferenceMoreThanHr(downloadDate, lastUpdateDate)) { - if ( - moment(downloadDate).diff( - moment(lastUpdateDate), - "minutes" - ) >= -2 - ) { - return ( - <span className="text-warning"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet used for anyenforcement." - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.tagActivationTime)} - </span> - ); - } else { - return ( - <span className="text-error"> - <CustomTooltip - placement="bottom" - content={ - "Policy is updated but not yet used for anyenforcement." - } - icon="fa-fw fa fa-exclamation-circle active-policy-alert" - /> - {setTimeStamp(original.info.tagActivationTime)}{" "} - </span> - ); - } - } - } - return setTimeStamp(original.info.tagActivationTime); }, minWidth: 190 } @@ -564,7 +474,11 @@ function Plugin_Status() { </div> </Col> </Row> - <AuditFilterEntries entries={entries} refreshTable={refreshTable} /> + <Row> + <Col sm={11}> + <AuditFilterEntries entries={entries} refreshTable={refreshTable} /> + </Col> + </Row> <XATableLayout data={pluginStatusListingData} columns={columns} @@ -574,6 +488,7 @@ function Plugin_Status() { columnSort={true} clientSideSorting={true} showPagination={false} + columnHide={true} /> </React.Fragment> </div> diff --git a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginsLog.jsx b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginsLog.jsx index 53e50791b..d6f8b433d 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginsLog.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/PluginsLog.jsx @@ -24,7 +24,7 @@ import XATableLayout from "Components/XATableLayout"; import { AuditFilterEntries } from "Components/CommonComponents"; import moment from "moment-timezone"; import dateFormat from "dateformat"; -import { sortBy, filter } from "lodash"; +import { sortBy, filter, isEmpty } from "lodash"; import StructuredFilter from "../../components/structured-filter/react-typeahead/tokenizer"; import { fetchApi } from "Utils/fetchAPI"; import { @@ -168,13 +168,15 @@ function Plugins() { Header: "Plugin ID", accessor: "agentId", Cell: (rawValue) => { - return ( + return !isEmpty(rawValue.value) ? ( <span className="text-truncate text-center d-block" title={rawValue.value} > {rawValue.value} </span> + ) : ( + <span className="text-center d-block">--</span> ); }, disableSortBy: true @@ -198,13 +200,15 @@ function Plugins() { Header: "Cluster Name", accessor: "clusterName", Cell: (rawValue) => { - return ( + return !isEmpty(rawValue.value) ? ( <span className="text-truncate text-center d-block" title={rawValue.value} > {rawValue.value} </span> + ) : ( + <span className="text-center d-block">--</span> ); }, width: 100,
