This is an automated email from the ASF dual-hosted git repository. dhavalshah9131 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ranger.git
commit 69911e5258d8b611e1410698c42e9ef59c907caa Author: Mugdha Varadkar <mug...@apache.org> AuthorDate: Thu Nov 9 13:38:35 2023 +0530 RANGER-4187: Not able to search using multiple user filter in access audit tab --- .../react-typeahead/tokenizer/index.js | 25 ++++- .../main/webapp/react-webapp/src/utils/XAUtils.js | 119 ++++++++++++++++----- .../src/views/AuditEvent/AccessLogs.jsx | 87 ++++++++++----- 3 files changed, 174 insertions(+), 57 deletions(-) diff --git a/security-admin/src/main/webapp/react-webapp/src/components/structured-filter/react-typeahead/tokenizer/index.js b/security-admin/src/main/webapp/react-webapp/src/components/structured-filter/react-typeahead/tokenizer/index.js index 82f73024a..c17a3ff5e 100644 --- a/security-admin/src/main/webapp/react-webapp/src/components/structured-filter/react-typeahead/tokenizer/index.js +++ b/security-admin/src/main/webapp/react-webapp/src/components/structured-filter/react-typeahead/tokenizer/index.js @@ -23,7 +23,7 @@ import KeyEvent from "../keyevent"; import Typeahead from "../typeahead"; import createReactClass from "create-react-class"; import PropTypes from "prop-types"; -import { find, map, some, trim } from "lodash"; +import { find, filter, map, some, trim, includes } from "lodash"; var classNames = require("classnames"); /** * A typeahead that, when an option is selected, instead of simply filling @@ -74,7 +74,7 @@ var TypeaheadTokenizer = createReactClass({ !!this.props.customClasses.token; var classList = classNames(tokenClasses); var result = this.state.selected.map(function (selected, index) { - let mykey = selected.category + selected.value; + let mykey = selected.category + selected.value + index; let categoryLabel = this._getFilterCategoryLabel(selected.category); let categoryValue = this._getFilterCategoryLabelForOption( selected.category, @@ -113,7 +113,15 @@ var TypeaheadTokenizer = createReactClass({ if (this.state.category == "") { var categories = []; let selectedCategory = []; - selectedCategory = map(this.state.selected, "category"); + let bypassCategory = map( + filter(this.props.options, ["addMultiple", true]), + "category" + ); + selectedCategory = this.state.selected + .map((item) => { + if (!bypassCategory.includes(item.category)) return item.category; + }) + .filter(Boolean); for (var i = 0; i < this.props.options.length; i++) { selectedCategory.indexOf(this.props.options[i].category) === -1 && categories.push(this.props.options[i].category); @@ -245,7 +253,16 @@ var TypeaheadTokenizer = createReactClass({ _getOptionsLabel: function () { var currentHeader = this._getHeader(); var optionsLabel = []; - let selectedCategory = map(this.state.selected, "category"); + let selectedCategory = []; + let bypassCategory = map( + filter(this.props.options, ["addMultiple", true]), + "category" + ); + selectedCategory = this.state.selected + .map((item) => { + if (!bypassCategory.includes(item.category)) return item.category; + }) + .filter(Boolean); if (currentHeader == "Category") { for (var i = 0; i < this.props.options.length; i++) { selectedCategory.indexOf(this.props.options[i].category) === -1 && 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 ebf1b3fc2..9f6706261 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 @@ -41,7 +41,8 @@ import { isNull, some, has, - sortBy + sortBy, + isArray } from "lodash"; import { matchRoutes } from "react-router-dom"; import dateFormat from "dateformat"; @@ -1141,28 +1142,49 @@ export const fetchSearchFilterParams = ( let defaultSearchFilterParam = []; // Get search filter params from current search params - const currentParams = Object.fromEntries([...searchParams]); - for (const param in currentParams) { + for (const [key, value] of searchParams.entries()) { let searchFilterObj = find(searchFilterOptions, { - urlLabel: param + urlLabel: key }); if (!isUndefined(searchFilterObj)) { let category = searchFilterObj.category; - let value = currentParams[param]; + let categoryValue = value; + + if (searchFilterObj?.addMultiple) { + let oldValue = searchFilterParam[category]; + let newValue = value; + if (oldValue) { + if (isArray(oldValue)) { + searchFilterParam[category].push(newValue); + searchParam[key].push(newValue); + } else { + searchFilterParam[category] = [oldValue, newValue]; + searchParam[key] = [oldValue, newValue]; + } + } else { + searchFilterParam[category] = newValue; + searchParam[key] = newValue; + } + } else { + if (searchFilterObj.type == "textoptions") { + let textOptionObj = find(searchFilterObj.options(), { + label: categoryValue + }); + categoryValue = !isUndefined(textOptionObj) + ? textOptionObj.value + : categoryValue; + } - if (searchFilterObj.type == "textoptions") { - let textOptionObj = find(searchFilterObj.options(), { - label: value - }); - value = !isUndefined(textOptionObj) ? textOptionObj.value : value; + searchFilterParam[category] = categoryValue; + searchParam[key] = value; } - - searchFilterParam[category] = value; defaultSearchFilterParam.push({ category: category, - value: value + value: categoryValue }); + } else { + searchParam[key] = value; } } @@ -1182,26 +1204,49 @@ export const fetchSearchFilterParams = ( let category = searchFilterObj.category; let value = localStorageParams[localParam]; - if (searchFilterObj.type == "textoptions") { - let textOptionObj = find(searchFilterObj.options(), { - label: value + if (searchFilterObj?.addMultiple) { + if (isArray(value)) { + for (const val of value) { + searchFilterParam[category] = value; + defaultSearchFilterParam.push({ + category: category, + value: val + }); + searchParam[localParam] = value; + } + } else { + searchFilterParam[category] = value; + defaultSearchFilterParam.push({ + category: category, + value: value + }); + searchParam[localParam] = value; + } + } else { + if (searchFilterObj.type == "textoptions") { + let textOptionObj = find(searchFilterObj.options(), { + label: value + }); + value = !isUndefined(textOptionObj) ? textOptionObj.value : value; + } + + searchFilterParam[category] = value; + defaultSearchFilterParam.push({ + category: category, + value: value }); - value = !isUndefined(textOptionObj) ? textOptionObj.value : value; + searchParam[localParam] = localStorageParams[localParam]; } - - searchFilterParam[category] = value; - defaultSearchFilterParam.push({ - category: category, - value: value - }); + } else { searchParam[localParam] = localStorageParams[localParam]; } } } } + finalSearchFilterData["searchFilterParam"] = searchFilterParam; finalSearchFilterData["defaultSearchFilterParam"] = defaultSearchFilterParam; - finalSearchFilterData["searchParam"] = { ...currentParams, ...searchParam }; + finalSearchFilterData["searchParam"] = searchParam; return finalSearchFilterData; }; @@ -1217,7 +1262,21 @@ export const parseSearchFilter = (filter, searchFilterOptions) => { }); if (searchFilterObj !== undefined) { - searchFilterParam[obj.category] = obj.value; + if (searchFilterObj?.addMultiple) { + let oldValue = searchFilterParam[obj.category]; + let newValue = obj.value; + if (oldValue) { + if (isArray(oldValue)) { + searchFilterParam[obj.category].push(newValue); + } else { + searchFilterParam[obj.category] = [oldValue, newValue]; + } + } else { + searchFilterParam[obj.category] = newValue; + } + } else { + searchFilterParam[obj.category] = obj.value; + } let urlLabelParam = searchFilterObj.urlLabel; @@ -1228,7 +1287,12 @@ export const parseSearchFilter = (filter, searchFilterOptions) => { searchParam[urlLabelParam] = textOptionObj !== undefined ? textOptionObj.label : obj.value; } else { - searchParam[urlLabelParam] = obj.value; + if (searchFilterObj?.addMultiple) { + searchParam[urlLabelParam] = + searchFilterParam[searchFilterObj.category]; + } else { + searchParam[urlLabelParam] = obj.value; + } } } }); @@ -1352,9 +1416,8 @@ export const updateTagActive = (isTagView) => { }; export const handleLogout = async (checkKnoxSSOVal, navigate) => { - let logoutResp = {}; try { - logoutResp = await fetchApi({ + await fetchApi({ url: "logout", baseURL: "", headers: { diff --git a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AccessLogs.jsx b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AccessLogs.jsx index 7ec8503e7..141b06cdb 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AccessLogs.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/AuditEvent/AccessLogs.jsx @@ -18,7 +18,7 @@ */ import React, { useState, useCallback, useEffect, useRef } from "react"; -import { useSearchParams, useOutletContext } from "react-router-dom"; +import { useSearchParams, useOutletContext, Link } from "react-router-dom"; import { Badge, Button, Row, Col, Table, Modal } from "react-bootstrap"; import XATableLayout from "Components/XATableLayout"; import dateFormat from "dateformat"; @@ -40,10 +40,12 @@ import { toString, toUpper, has, - filter + filter, + find, + isArray } from "lodash"; import { toast } from "react-toastify"; -import { Link } from "react-router-dom"; +import qs from "qs"; import { AccessMoreLess } from "Components/CommonComponents"; import { PolicyViewDetails } from "./AdminLogs/PolicyViewDetails"; import StructuredFilter from "../../components/structured-filter/react-typeahead/tokenizer"; @@ -120,10 +122,13 @@ function Access() { }); } - // Updating the states for search params, search filter, default search filter and localStorage - if (localStorage?.excludeServiceUser) { + // Add excludeServiceUser if not present in the search param with default state value of checked + if (!has(searchParam, "excludeServiceUser")) { searchParam["excludeServiceUser"] = checked; } + localStorage.setItem("excludeServiceUser", checked); + + // Updating the states for search params, search filter, default search filter and localStorage setSearchParams(searchParam, { replace: true }); setSearchFilterParams(searchFilterParam); setDefaultSearchFilterParams(defaultSearchFilterParam); @@ -135,17 +140,16 @@ function Access() { let { searchFilterParam, defaultSearchFilterParam, searchParam } = fetchSearchFilterParams("bigData", searchParams, searchFilterOptions); - // Updating the states for search params, search filter, default search filter and localStorage - if (localStorage?.excludeServiceUser || searchParam?.excludeServiceUser) { - if (searchParam?.excludeServiceUser) { - setChecked(searchParam?.excludeServiceUser == "true" ? true : false); - localStorage.setItem( - "excludeServiceUser", - searchParam?.excludeServiceUser - ); - } - searchParam["excludeServiceUser"] = localStorage?.excludeServiceUser; + // Update excludeServiceUser in the search param and in the localStorage + if (searchParam?.excludeServiceUser) { + setChecked(searchParam?.excludeServiceUser == "true" ? true : false); + localStorage.setItem( + "excludeServiceUser", + searchParam?.excludeServiceUser + ); } + + // Updating the states for search params, search filter, default search filter and localStorage setSearchParams(searchParam, { replace: true }); if ( JSON.stringify(searchFilterParams) !== JSON.stringify(searchFilterParam) @@ -154,6 +158,7 @@ function Access() { } setDefaultSearchFilterParams(defaultSearchFilterParam); localStorage.setItem("bigData", JSON.stringify(searchParam)); + setContentLoader(false); } }, [searchParams, servicesAvailable]); @@ -186,7 +191,10 @@ function Access() { try { logsResp = await fetchApi({ url: "assets/accessAudit", - params: params + params: params, + paramsSerializer: function (params) { + return qs.stringify(params, { arrayFormat: "repeat" }); + } }); logs = logsResp.data.vXAccessAudits; totalCount = logsResp.data.totalCount; @@ -223,13 +231,39 @@ function Access() { }; const toggleChange = (chkVal) => { - let currentParams = Object.fromEntries([...searchParams]); - currentParams["excludeServiceUser"] = chkVal?.target?.checked; - localStorage.setItem( - "excludeServiceUser", - JSON.stringify(chkVal?.target?.checked) - ); - setSearchParams(currentParams, { replace: true }); + let checkBoxValue = chkVal?.target?.checked; + let searchParam = {}; + + for (const [key, value] of searchParams.entries()) { + let searchFilterObj = find(searchFilterOptions, { + urlLabel: key + }); + + if (!isUndefined(searchFilterObj)) { + if (searchFilterObj?.addMultiple) { + let oldValue = searchParam[key]; + let newValue = value; + if (oldValue) { + if (isArray(oldValue)) { + searchParam[key].push(newValue); + } else { + searchParam[key] = [oldValue, newValue]; + } + } else { + searchParam[key] = newValue; + } + } else { + searchParam[key] = value; + } + } else { + searchParam[key] = value; + } + } + + searchParam["excludeServiceUser"] = checkBoxValue; + localStorage.setItem("excludeServiceUser", checkBoxValue); + + setSearchParams(searchParam, { replace: true }); setAccessLogs([]); setChecked(chkVal?.target?.checked); setLoader(true); @@ -238,6 +272,7 @@ function Access() { const handleClosePolicyId = () => setPolicyViewModal(false); const handleClose = () => setShowRowModal(false); + const rowModal = (row) => { setShowRowModal(true); setRowData(row.original); @@ -770,7 +805,7 @@ function Access() { category: "eventId", label: "Audit ID", urlLabel: "eventId", - type: "number" + type: "text" }, { category: "clientIP", @@ -794,7 +829,8 @@ function Access() { category: "excludeUser", label: "Exclude User", urlLabel: "excludeUser", - type: "number" + addMultiple: true, + type: "text" }, { category: "policyId", @@ -857,6 +893,7 @@ function Access() { category: "requestUser", label: "User", urlLabel: "user", + addMultiple: true, type: "text" }, {