From 4eb92856dd947bc5b768d17bf1a44eab99f2148a Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Thu, 29 Feb 2024 18:06:04 +0100 Subject: [PATCH 01/26] fixing gantt chart, new driver version was returning {low,high} numbers --- src/extensions/advancedcharts/chart/gantt/Utils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/extensions/advancedcharts/chart/gantt/Utils.ts b/src/extensions/advancedcharts/chart/gantt/Utils.ts index 012c77201..331be20ff 100644 --- a/src/extensions/advancedcharts/chart/gantt/Utils.ts +++ b/src/extensions/advancedcharts/chart/gantt/Utils.ts @@ -1,3 +1,4 @@ +import { toNumber } from '../../../../chart/ChartUtils'; import { buildGraphVisualizationObjectFromRecords } from '../../../../chart/graph/util/RecordUtils'; import date_utils from './frappe/lib/date_utils'; @@ -97,9 +98,9 @@ export function createTasksList( return undefined; } } - return { - start: new Date(neoStartDate.year, neoStartDate.month, neoStartDate.day), - end: new Date(neoEndDate.year, neoEndDate.month, neoEndDate.day), + let res = { + start: new Date(toNumber(neoStartDate.year), toNumber(neoStartDate.month), toNumber(neoStartDate.day)), + end: new Date(toNumber(neoEndDate.year), toNumber(neoEndDate.month), toNumber(neoEndDate.day)), name: name || '(undefined)', labels: n.labels, dependencies: dependencies[n.id], @@ -112,6 +113,7 @@ export function createTasksList( isDisabled: true, styles: { progressColor: '#ffbb54', progressSelectedColor: '#ff9e0d' }, }; + return res; }) .filter((i) => i !== undefined); } From f417f57ae7c3bc7e4695e13168be75fd6250551d Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Thu, 29 Feb 2024 18:13:57 +0100 Subject: [PATCH 02/26] Revert "fixing gantt chart, new driver version was returning {low,high} numbers" This reverts commit 4eb92856dd947bc5b768d17bf1a44eab99f2148a. --- src/extensions/advancedcharts/chart/gantt/Utils.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/extensions/advancedcharts/chart/gantt/Utils.ts b/src/extensions/advancedcharts/chart/gantt/Utils.ts index 331be20ff..012c77201 100644 --- a/src/extensions/advancedcharts/chart/gantt/Utils.ts +++ b/src/extensions/advancedcharts/chart/gantt/Utils.ts @@ -1,4 +1,3 @@ -import { toNumber } from '../../../../chart/ChartUtils'; import { buildGraphVisualizationObjectFromRecords } from '../../../../chart/graph/util/RecordUtils'; import date_utils from './frappe/lib/date_utils'; @@ -98,9 +97,9 @@ export function createTasksList( return undefined; } } - let res = { - start: new Date(toNumber(neoStartDate.year), toNumber(neoStartDate.month), toNumber(neoStartDate.day)), - end: new Date(toNumber(neoEndDate.year), toNumber(neoEndDate.month), toNumber(neoEndDate.day)), + return { + start: new Date(neoStartDate.year, neoStartDate.month, neoStartDate.day), + end: new Date(neoEndDate.year, neoEndDate.month, neoEndDate.day), name: name || '(undefined)', labels: n.labels, dependencies: dependencies[n.id], @@ -113,7 +112,6 @@ export function createTasksList( isDisabled: true, styles: { progressColor: '#ffbb54', progressSelectedColor: '#ff9e0d' }, }; - return res; }) .filter((i) => i !== undefined); } From 825b1e95d2af417c95c2e19b87d04a09a9de95bd Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Thu, 29 Feb 2024 18:15:01 +0100 Subject: [PATCH 03/26] fixing gantt chart, new driver version was returning {low,high} numbers --- src/extensions/advancedcharts/chart/gantt/Utils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/extensions/advancedcharts/chart/gantt/Utils.ts b/src/extensions/advancedcharts/chart/gantt/Utils.ts index 012c77201..331be20ff 100644 --- a/src/extensions/advancedcharts/chart/gantt/Utils.ts +++ b/src/extensions/advancedcharts/chart/gantt/Utils.ts @@ -1,3 +1,4 @@ +import { toNumber } from '../../../../chart/ChartUtils'; import { buildGraphVisualizationObjectFromRecords } from '../../../../chart/graph/util/RecordUtils'; import date_utils from './frappe/lib/date_utils'; @@ -97,9 +98,9 @@ export function createTasksList( return undefined; } } - return { - start: new Date(neoStartDate.year, neoStartDate.month, neoStartDate.day), - end: new Date(neoEndDate.year, neoEndDate.month, neoEndDate.day), + let res = { + start: new Date(toNumber(neoStartDate.year), toNumber(neoStartDate.month), toNumber(neoStartDate.day)), + end: new Date(toNumber(neoEndDate.year), toNumber(neoEndDate.month), toNumber(neoEndDate.day)), name: name || '(undefined)', labels: n.labels, dependencies: dependencies[n.id], @@ -112,6 +113,7 @@ export function createTasksList( isDisabled: true, styles: { progressColor: '#ffbb54', progressSelectedColor: '#ff9e0d' }, }; + return res; }) .filter((i) => i !== undefined); } From a81b98036adc203c6652d3f52e30f04474f770c8 Mon Sep 17 00:00:00 2001 From: Bennu Date: Fri, 1 Mar 2024 11:11:17 +0100 Subject: [PATCH 04/26] NaNs --- src/chart/graph/util/RecordUtils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/chart/graph/util/RecordUtils.ts b/src/chart/graph/util/RecordUtils.ts index f432e6596..b8d853d33 100644 --- a/src/chart/graph/util/RecordUtils.ts +++ b/src/chart/graph/util/RecordUtils.ts @@ -1,6 +1,6 @@ import { evaluateRulesOnNode, evaluateRulesOnLink } from '../../../extensions/styling/StyleRuleEvaluator'; import { extractNodePropertiesFromRecords, mergeNodePropsFieldsLists } from '../../../report/ReportRecordProcessing'; -import { valueIsArray, valueIsNode, valueIsRelationship, valueIsPath } from '../../ChartUtils'; +import { valueIsArray, valueIsNode, valueIsRelationship, valueIsPath, toNumber } from '../../ChartUtils'; import { GraphChartVisualizationProps } from '../GraphChartVisualization'; import { assignCurvatureToLink } from './RelUtils'; import { isNode } from 'neo4j-driver-core/lib/graph-types.js'; @@ -49,7 +49,9 @@ function extractGraphEntitiesFromField( nodes[value.identity.low] = { id: value.identity.low, labels: value.labels, - size: value.properties[nodeSizeProperty] ? value.properties[nodeSizeProperty] : defaultNodeSize, + size: !Number.isNaN(value.properties[nodeSizeProperty]) + ? toNumber(value.properties[nodeSizeProperty]) + : defaultNodeSize, properties: value.properties, mainLabel: value.labels[value.labels.length - 1], }; @@ -67,7 +69,9 @@ function extractGraphEntitiesFromField( source: value.start.low, target: value.end.low, type: value.type, - width: value.properties[relWidthProperty] ? value.properties[relWidthProperty] : defaultRelWidth, + width: !Number.isNaN(value.properties[relWidthProperty]) + ? toNumber(value.properties[relWidthProperty]) + : defaultRelWidth, color: value.properties[relColorProperty] ? value.properties[relColorProperty] : defaultRelColor, properties: value.properties, }); From d2729d7a4f4dcb2aa32c418a82d49f46bda44f4c Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Fri, 1 Mar 2024 11:42:16 +0100 Subject: [PATCH 05/26] fixing number parsing --- src/chart/ChartUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chart/ChartUtils.ts b/src/chart/ChartUtils.ts index cd6feb8c0..dd7eaf6b4 100644 --- a/src/chart/ChartUtils.ts +++ b/src/chart/ChartUtils.ts @@ -95,7 +95,7 @@ export function valueIsObject(value) { } export function toNumber(ref) { - if (ref === undefined) { + if (ref === undefined || typeof ref === 'number') { return ref; } let { low, high } = ref; @@ -172,7 +172,7 @@ export const downloadCSV = (rows) => { }); csv += '\n'; }); - const file = new Blob([`\ufeff${ csv}`], { type: 'text/plain;charset=utf8' }); + const file = new Blob([`\ufeff${csv}`], { type: 'text/plain;charset=utf8' }); element.href = URL.createObjectURL(file); element.download = 'table.csv'; document.body.appendChild(element); // Required for this to work in FireFox From 2211bb7d23072d6517567da3248e0b89bd5da132 Mon Sep 17 00:00:00 2001 From: Niels de Jong Date: Mon, 4 Mar 2024 19:48:56 +0100 Subject: [PATCH 06/26] Revert "Handle token expiration in SSO (#611)" (#815) This reverts commit 001cee1cfe80a21f7d348cea03a74e1328cbe5df. --- package.json | 1 - src/application/ApplicationActions.ts | 7 +-- src/application/ApplicationReducer.ts | 4 +- src/application/ApplicationThunks.ts | 70 +++------------------------ src/component/sso/SSOUtils.ts | 2 +- src/dashboard/Dashboard.tsx | 7 +-- yarn.lock | 30 ------------ 7 files changed, 13 insertions(+), 108 deletions(-) diff --git a/package.json b/package.json index 7632e3c79..1b3a74b3b 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "mui-color": "^2.0.0-beta.2", "mui-nested-menu": "^3.2.1", "neo4j-client-sso": "^1.2.2", - "neo4j-driver": "^5.12.0", "openai": "^3.3.0", "postcss": "^8.4.21", "postcss-loader": "^7.2.4", diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 5d7f815dc..0b556f2dc 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -2,8 +2,6 @@ * This file contains all state-changing actions relevant for the main application. */ -import { SSOProviderOriginal } from 'neo4j-client-sso'; - export const CLEAR_NOTIFICATION = 'APPLICATION/CLEAR_NOTIFICATION'; export const clearNotification = () => ({ type: CLEAR_NOTIFICATION, @@ -58,11 +56,10 @@ export const setConnectionProperties = ( port: string, database: string, username: string, - password: string, - ssoProviders?: SSOProviderOriginal[] + password: string ) => ({ type: SET_CONNECTION_PROPERTIES, - payload: { protocol, url, port, database, username, password, ssoProviders }, + payload: { protocol, url, port, database, username, password }, }); export const SET_BASIC_CONNECTION_PROPERTIES = 'APPLICATION/SET_BASIC_CONNECTION_PROPERTIES'; diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 590e930db..e662ade63 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -64,7 +64,6 @@ const initialState = { database: '', username: 'neo4j', password: '', - ssoProviders: [], }, shareDetails: undefined, desktopConnection: null, @@ -247,7 +246,7 @@ export const applicationReducer = (state = initialState, action: { type: any; pa return state; } case SET_CONNECTION_PROPERTIES: { - const { protocol, url, port, database, username, password, ssoProviders } = payload; + const { protocol, url, port, database, username, password } = payload; state = update(state, { connection: { protocol: protocol, @@ -256,7 +255,6 @@ export const applicationReducer = (state = initialState, action: { type: any; pa database: database, username: username, password: password, - ssoProviders, }, }); return state; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 965d7fc78..fc880fd3a 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -1,3 +1,4 @@ +import { createDriver } from 'use-neo4j'; import { initializeSSO } from '../component/sso/SSOUtils'; import { DEFAULT_SCREEN, Screens } from '../config/ApplicationConfig'; import { setDashboard } from '../dashboard/DashboardActions'; @@ -43,9 +44,6 @@ import { } from './ApplicationActions'; import { setLoggingMode, setLoggingDatabase, setLogErrorNotification } from './logging/LoggingActions'; import { version } from '../modal/AboutModal'; -import neo4j, { auth, authTokenManagers } from 'neo4j-driver'; -import type { Neo4jScheme } from 'use-neo4j/dist/neo4j-config.interface'; -import { SSOProviderOriginal, handleRefreshingToken } from 'neo4j-client-sso'; import { applicationIsStandalone } from './ApplicationSelectors'; import { applicationGetLoggingSettings } from './logging/LoggingSelectors'; import { createLogThunk } from './logging/LoggingThunk'; @@ -56,47 +54,6 @@ import { createUUID } from '../utils/uuid'; * Several actions/other thunks may be dispatched from here. */ -export const createDriver = ( - scheme: Neo4jScheme, - host: string, - port: string | number, - username?: string, - password?: string, - config?: { userAgent?: string }, - ssoProviders: SSOProviderOriginal[] = [] -) => { - if (ssoProviders.length > 0) { - const authTokenMgr = authTokenManagers.bearer({ - tokenProvider: async () => { - const credentials = await handleRefreshingToken(ssoProviders); - const token = auth.bearer(credentials.password); - // Get the expiration from the JWT's payload, which is a JSON string encoded - // using base64. You could also use a JWT parsing lib - const [, payloadBase64] = credentials.password.split('.'); - const payload: unknown = JSON.parse(window.atob(payloadBase64 ?? '')); - let expiration: Date; - if (typeof payload === 'object' && payload !== null && 'exp' in payload) { - expiration = new Date(Number(payload.exp) * 1000); - } else { - expiration = new Date(); - } - - return { - expiration, - token, - }; - }, - }); - return neo4j.driver(`${scheme}://${host}:${port}`, authTokenMgr, config); - } - - if (!username || !password) { - return neo4j.driver(`${scheme}://${host}:${port}`); - } - - return neo4j.driver(`${scheme}://${host}:${port}`, neo4j.auth.basic(username, password), config); -}; - /** * Establish a connection to Neo4j with the specified credentials. Open/close the relevant windows when connection is made (un)successfully. * @param protocol - the neo4j protocol (e.g. bolt, bolt+s, neo4j+s, ...) @@ -105,24 +62,14 @@ export const createDriver = ( * @param database - the Neo4j database to connect to. * @param username - Neo4j username. * @param password - Neo4j password. - * @param SSOProviders - List of available SSO providers */ export const createConnectionThunk = - (protocol, url, port, database, username, password, SSOProviders = []) => - (dispatch: any, getState: any) => { + (protocol, url, port, database, username, password) => (dispatch: any, getState: any) => { const loggingState = getState(); const loggingSettings = applicationGetLoggingSettings(loggingState); const neodashMode = applicationIsStandalone(loggingState) ? 'Standalone' : 'Editor'; try { - const driver = createDriver( - protocol, - url, - port, - username, - password, - { userAgent: `neodash/v${version}` }, - SSOProviders - ); + const driver = createDriver(protocol, url, port, username, password, { userAgent: `neodash/v${version}` }); // eslint-disable-next-line no-console console.log('Attempting to connect...'); const validateConnection = (records) => { @@ -561,7 +508,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: dispatch(setAboutModalOpen(false)); dispatch(setConnected(false)); dispatch(setWelcomeScreenOpen(false)); - const success = await initializeSSO(state.application.cachedSSODiscoveryUrl, (credentials, ssoProviders) => { + const success = await initializeSSO(state.application.cachedSSODiscoveryUrl, (credentials) => { if (standalone) { // Redirected from SSO and running in viewer mode, merge retrieved config with hardcoded credentials. dispatch( @@ -571,8 +518,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: config.standalonePort, config.standaloneDatabase, credentials.username, - credentials.password, - ssoProviders + credentials.password ) ); dispatch( @@ -582,8 +528,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: config.standalonePort, config.standaloneDatabase, credentials.username, - credentials.password, - ssoProviders + credentials.password ) ); } else { @@ -595,8 +540,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: state.application.connection.port, state.application.connection.database, credentials.username, - credentials.password, - ssoProviders + credentials.password ) ); dispatch(setConnected(true)); diff --git a/src/component/sso/SSOUtils.ts b/src/component/sso/SSOUtils.ts index b9b6f038c..c25e0d086 100644 --- a/src/component/sso/SSOUtils.ts +++ b/src/component/sso/SSOUtils.ts @@ -118,7 +118,7 @@ export const initializeSSO = async (cachedSSODiscoveryUrl, _setCredentials) => { // Successful credentials retrieval. // Log in at the Neo4j dbms now using the Neo4j (js) driver. // - _setCredentials(credentials, mergedSSOProviders); + _setCredentials(credentials); // Exemplifying retrieval of stored URL paramenters _retrieveAdditionalURLParameters(); diff --git a/src/dashboard/Dashboard.tsx b/src/dashboard/Dashboard.tsx index acc4bb712..1039fce91 100644 --- a/src/dashboard/Dashboard.tsx +++ b/src/dashboard/Dashboard.tsx @@ -3,7 +3,7 @@ import NeoPage from '../page/Page'; import NeoDashboardHeader from './header/DashboardHeader'; import NeoDashboardTitle from './header/DashboardTitle'; import NeoDashboardHeaderPageList from './header/DashboardHeaderPageList'; -import { Neo4jProvider } from 'use-neo4j'; +import { createDriver, Neo4jProvider } from 'use-neo4j'; import { applicationGetConnection, applicationGetStandaloneSettings } from '../application/ApplicationSelectors'; import { connect } from 'react-redux'; import NeoDashboardConnectionUpdateHandler from '../component/misc/DashboardConnectionUpdateHandler'; @@ -11,7 +11,6 @@ import { forceRefreshPage } from '../page/PageActions'; import { getPageNumber } from '../settings/SettingsSelectors'; import { createNotificationThunk } from '../page/PageThunks'; import { version } from '../modal/AboutModal'; -import { createDriver } from '../application/ApplicationThunks'; import NeoDashboardSidebar from './sidebar/DashboardSidebar'; const Dashboard = ({ @@ -33,10 +32,8 @@ const Dashboard = ({ connection.port, connection.username, connection.password, - { userAgent: `neodash/v${version}` }, - connection.ssoProviders + { userAgent: `neodash/v${version}` } ); - // @ts-ignore wrong driver version setDriver(newDriver); } const content = ( diff --git a/yarn.lock b/yarn.lock index c5dcfb1f6..d9935f9e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10435,15 +10435,6 @@ neo4j-client-sso@^1.2.2: jwt-decode "^3.1.2" lodash.pick "^4.4.0" -neo4j-driver-bolt-connection@5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-5.12.0.tgz#aff161367d287579d7bdd3ee4179eed324398210" - integrity sha512-dlYbFsfT0HopGItitG5uDK4nAkcqSPNtRqMz318qy//7fb/7OXVLGYikj57Ve1toJiJD8IIVErt/dVuEUHVxGA== - dependencies: - buffer "^6.0.3" - neo4j-driver-core "5.12.0" - string_decoder "^1.3.0" - neo4j-driver-bolt-connection@^4.4.10: version "4.4.10" resolved "https://registry.yarnpkg.com/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-4.4.10.tgz#a8b5b7f82b1d6f9a71a43eafcb0e21512ea24908" @@ -10453,11 +10444,6 @@ neo4j-driver-bolt-connection@^4.4.10: neo4j-driver-core "^4.4.10" string_decoder "^1.3.0" -neo4j-driver-core@5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/neo4j-driver-core/-/neo4j-driver-core-5.12.0.tgz#1f8616da7e945921574811368a68f5d2501bfd35" - integrity sha512-xBRi5oezysDUvtvBiIgBchzumkDZxvR9ol9sUtA9PBgVENeSmPH3CncitY8S979CFELS6wH7kydcjPLB4QMOzA== - neo4j-driver-core@^4.4.10: version "4.4.10" resolved "https://registry.yarnpkg.com/neo4j-driver-core/-/neo4j-driver-core-4.4.10.tgz#6f4c1ccc1199f864b149bdcef5e50e45ff95c29e" @@ -10473,15 +10459,6 @@ neo4j-driver@^4.4.5: neo4j-driver-core "^4.4.10" rxjs "^6.6.3" -neo4j-driver@^5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-5.12.0.tgz#1b2d7db1672ad224f0146542efee306a0a156a11" - integrity sha512-T2Vz63XDkL9TomM16dBusuXbo7d9SIGw2g3VR/rmrWTdbl1V1LYFx/u1P7AwBsFuX08oncKHfZwHGsWrCvdMyA== - dependencies: - neo4j-driver-bolt-connection "5.12.0" - neo4j-driver-core "5.12.0" - rxjs "^7.8.1" - next-tick@1, next-tick@^1.0.0, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -12311,13 +12288,6 @@ rxjs@^7.5.1, rxjs@^7.5.5, rxjs@^7.8.0: dependencies: tslib "^2.1.0" -rxjs@^7.8.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" From 4b779a0cd3e9623c6fb6c3bd1d7b27b74f893986 Mon Sep 17 00:00:00 2001 From: alfredorubin96 <103421036+alfredorubin96@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:57:12 +0100 Subject: [PATCH 07/26] Fixing RBAC feature + parameters (#813) * fixed race condition, to work on other points in PR * Fixes for complex parameter types in forms * Added special case for handling cross-db label access * handling fixed grants without non-fixed grants * Added error handling to RBAC extension * Added back async modifier * bug fixin grbac --------- Co-authored-by: Alfred Rubin Co-authored-by: Niels de Jong --- src/extensions/forms/FormsReportConfig.tsx | 2 +- src/extensions/forms/chart/NeoForm.tsx | 2 +- .../settings/NeoFormCardSettingsModal.tsx | 7 +- src/extensions/rbac/RBACManagementMenu.tsx | 7 +- src/extensions/rbac/RBACManagementModal.tsx | 96 +++++++++++++++++-- src/extensions/rbac/RBACUtils.ts | 94 +++++++++++++----- src/modal/ExportModal.tsx | 2 +- src/report/ReportQueryRunner.ts | 1 + 8 files changed, 170 insertions(+), 41 deletions(-) diff --git a/src/extensions/forms/FormsReportConfig.tsx b/src/extensions/forms/FormsReportConfig.tsx index 4367a7497..5918a4425 100644 --- a/src/extensions/forms/FormsReportConfig.tsx +++ b/src/extensions/forms/FormsReportConfig.tsx @@ -46,7 +46,7 @@ export const FORMS = { label: 'Clear parameters after submit', type: SELECTION_TYPES.LIST, values: [true, false], - default: false, + default: true, }, hasResetButton: { label: 'Has Reset Button', diff --git a/src/extensions/forms/chart/NeoForm.tsx b/src/extensions/forms/chart/NeoForm.tsx index e8d33641e..9ae9c9109 100644 --- a/src/extensions/forms/chart/NeoForm.tsx +++ b/src/extensions/forms/chart/NeoForm.tsx @@ -26,7 +26,7 @@ const NeoForm = (props: ChartProps) => { const hasResetButton = settings?.hasResetButton ?? true; const hasSubmitButton = settings?.hasSubmitButton ?? true; const hasSubmitMessage = settings?.hasSubmitMessage ?? true; - const clearParametersAfterSubmit = settings?.clearParametersAfterSubmit ?? false; + const clearParametersAfterSubmit = settings?.clearParametersAfterSubmit ?? true; const [submitButtonActive, setSubmitButtonActive] = React.useState(true); const [status, setStatus] = React.useState(FormStatus.DATA_ENTRY); const [formResults, setFormResults] = React.useState([]); diff --git a/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx b/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx index fc9fec0ad..87515c24b 100644 --- a/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx +++ b/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { Button, Dialog } from '@neo4j-ndl/react'; import ParameterSelectCardSettings from '../../../chart/parameter/ParameterSelectCardSettings'; import NeoCardSettingsFooter from '../../../card/settings/CardSettingsFooter'; +import { objMerge } from '../../../utils/ObjectManipulation'; const NeoFormCardSettingsModal = ({ open, setOpen, index, formFields, setFormFields, database, extensions }) => { const [advancedSettingsOpen, setAdvancedSettingsOpen] = React.useState(false); @@ -24,15 +25,19 @@ const NeoFormCardSettingsModal = ({ open, setOpen, index, formFields, setFormFie query={formFields[index].query} type={'select'} database={database} - settings={formFields[index].settings} + settings={objMerge({ inputMode: 'cypher' }, formFields[index].settings)} extensions={extensions} onReportSettingUpdate={(key, value) => { const newFormFields = [...formFields]; newFormFields[index].settings[key] = value; + if (key == 'type') { + newFormFields[index].type = value; + } setFormFields(newFormFields); }} onQueryUpdate={(query) => { const newFormFields = [...formFields]; + newFormFields[index].query = query; setFormFields(newFormFields); }} diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx index 264f84643..1a85b08fe 100644 --- a/src/extensions/rbac/RBACManagementMenu.tsx +++ b/src/extensions/rbac/RBACManagementMenu.tsx @@ -19,7 +19,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti if (!MenuOpen) { return; } - const query = `SHOW ROLES YIELD role WHERE role <> "PUBLIC" return role`; + const query = `SHOW PRIVILEGES YIELD role, action WHERE role <> "PUBLIC" RETURN role, 'dbms_actions' in collect(action)`; runCypherQuery( driver, 'system', @@ -32,7 +32,8 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti createNotification('Unable to retrieve roles', records[0].error); return; } - setRoles(records.map((record) => record._fields[0])); + // Only display roles which are not able to do 'dbms_actions', i.e. they are not admins. + setRoles(records.filter((r) => r._fields[1] == false).map((record) => record._fields[0])); } ); }, [MenuOpen]); @@ -71,7 +72,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti { setIsModalOpen(false); }} diff --git a/src/extensions/rbac/RBACManagementModal.tsx b/src/extensions/rbac/RBACManagementModal.tsx index c19f5ea21..178fa081e 100644 --- a/src/extensions/rbac/RBACManagementModal.tsx +++ b/src/extensions/rbac/RBACManagementModal.tsx @@ -26,6 +26,12 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti const [labels, setLabels] = useState([]); const [allowList, setAllowList] = useState([]); const [denyList, setDenyList] = useState([]); + const [fixedAllowList, setFixedAllowList] = useState([]); + const [fixedDenyList, setFixedDenyList] = useState([]); + const [denyCompleted, setDenyCompleted] = useState(false); + const [allowCompleted, setAllowCompleted] = useState(false); + const [usersCompleted, setUsersCompleted] = useState(false); + const [failed, setFailed] = useState(false); useEffect(() => { if (!open) { @@ -35,10 +41,23 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti setSelectedDatabase(''); return; } + setDenyCompleted(false); + setAllowCompleted(false); + setUsersCompleted(false); + setFailed(false); retrieveDatabaseList(driver, setDatabases); retrieveNeo4jUsers(driver, currentRole, setNeo4jUsers, setSelectedUsers); }, [open]); + useEffect(() => { + console.log([denyCompleted, allowCompleted, usersCompleted, failed]); + if (failed !== false) { + createNotification('Unable to update privileges', `${failed}`); + } else if (denyCompleted && allowCompleted && usersCompleted) { + createNotification('Success', `Access for role '${currentRole}' updated.`); + } + }, [denyCompleted, allowCompleted, usersCompleted, failed]); + const parseLabelsList = (database, records) => { const allLabels = records.map((record) => record._fields[0]).filter((l) => l !== '_Neodash_Dashboard'); retrieveAllowAndDenyLists( @@ -49,6 +68,8 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti setLabels, setAllowList, setDenyList, + setFixedAllowList, + setFixedDenyList, setLoaded ); }; @@ -58,16 +79,49 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti retrieveLabelsList(driver, selectedOption.value, (records) => parseLabelsList(selectedOption.value, records)); }; - const handleSave = () => { - updateUsers(driver, currentRole, neo4jUsers, selectedUsers); + const handleSave = async () => { + createNotification('Updating', `Access for role '${currentRole}' is being updated, please wait...`); + console.log(selectedUsers); + updateUsers( + driver, + currentRole, + neo4jUsers, + selectedUsers, + () => setUsersCompleted(true), + (failReason) => setFailed(`Operation 'ROLE-USER ASSIGNMENT' failed.\n Reason: ${failReason}`) + ); + if (selectedDatabase) { - createNotification('Updating', `Access for role '${currentRole}' is being updated, please wait...`); - updatePrivileges(driver, selectedDatabase, currentRole, labels, denyList, Operation.DENY, createNotification); - updatePrivileges(driver, selectedDatabase, currentRole, labels, allowList, Operation.GRANT, createNotification); + const nonFixedDenyList = denyList.filter((n) => !fixedDenyList.includes(n)); + const nonFixedAllowList = allowList.filter((n) => !fixedDenyList.includes(n)); + updatePrivileges( + driver, + selectedDatabase, + currentRole, + labels, + nonFixedDenyList, + Operation.DENY, + () => setDenyCompleted(true), + (failReason) => setFailed(`Operation 'DENY LABEL ACCESS' failed.\n Reason: ${failReason}`) + ).then(() => { + updatePrivileges( + driver, + selectedDatabase, + currentRole, + labels, + nonFixedAllowList, + Operation.GRANT, + () => setAllowCompleted(true), + (failReason) => setFailed(`Operation 'ALLOW LABEL ACCESS' failed.\n Reason: ${failReason}`) + ); + }); } else { - createNotification('Success', `Users have been updated for role '${currentRole}'.`); + // Since there is no database selected, we don't run the DENY/ALLOW queries. + // We just mark them as completed so the success message shows up. + setDenyCompleted(true); + setAllowCompleted(true); } - + console.log([denyCompleted, allowCompleted, usersCompleted, failed]); handleClose(); }; @@ -138,7 +192,17 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti value: allowList.map((nodelabel) => ({ value: nodelabel, label: nodelabel })), options: labels.map((nodelabel) => ({ value: nodelabel, label: nodelabel })), isMulti: true, - onChange: (val) => setAllowList(val.map((v) => v.value)), + onChange: (val) => { + // Make sure that only database-specific label access rules can be changed from this UI. + if (fixedAllowList.every((v) => val.map((selected) => selected.value).includes(v))) { + setAllowList(val.map((v) => v.value)); + } else { + createNotification( + 'Label cannot be removed', + 'The selected label is allowed access across all databases. You cannot remove this privilege using this interface.' + ); + } + }, }} /> @@ -154,9 +218,21 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti placeholder: 'Select labels', isClearable: false, value: denyList.map((nodelabel) => ({ value: nodelabel, label: nodelabel })), - options: labels.map((nodelabel) => ({ value: nodelabel, label: nodelabel })), + options: labels + .filter((l) => l !== '*') + .map((nodelabel) => ({ value: nodelabel, label: nodelabel })), isMulti: true, - onChange: (val) => setDenyList(val.map((v) => v.value)), + onChange: (val) => { + // Make sure that only database-specific label access rules can be changed from this UI. + if (fixedDenyList.every((v) => val.map((selected) => selected.value).includes(v))) { + setDenyList(val.map((v) => v.value)); + } else { + createNotification( + 'Label cannot be removed', + 'The selected label is denied access across all databases. You cannot remove this privilege using this interface.' + ); + } + }, }} /> diff --git a/src/extensions/rbac/RBACUtils.ts b/src/extensions/rbac/RBACUtils.ts index 0ed54781b..69e701bb9 100644 --- a/src/extensions/rbac/RBACUtils.ts +++ b/src/extensions/rbac/RBACUtils.ts @@ -15,15 +15,16 @@ export enum Operation { * @param newLabels list of new labels in the database, for which priveleges are changed. * @param operation The operation, either 'GRANT' or 'DENY' */ -export const updatePrivileges = ( +export async function updatePrivileges( driver, database, role, allLabels, newLabels, operation: Operation, - createNotification -) => { + onSuccess, + onFail +) { // TODO - should we also drop cross-database DENYs (`ON GRAPH *`) to catch the true full set? // TODO - there // 1. Special case for '*'. Create it if needed to be there, otherwise revoke it. @@ -49,7 +50,7 @@ export const updatePrivileges = ( {}, 1000, (status) => { - if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) { + if (status == QueryStatus.NO_DATA || status == QueryStatus.COMPLETE) { // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query. // We build in an artificial delay... const timeout = setTimeout(() => { @@ -67,21 +68,38 @@ export const updatePrivileges = ( ), {}, 1000, - () => { - if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) { - createNotification('Success', `Access for role '${role}' updated.`); + (status) => { + if (status == QueryStatus.NO_DATA || status == QueryStatus.COMPLETE) { + onSuccess(); + } + }, + (records) => { + if (records && records[0] && records[0].error) { + onFail(records[0].error); } } ); + } else { + onSuccess(); } }, 1000); } + }, + (records) => { + if (records && records[0] && records[0].error) { + onFail(records[0].error); + } } ); } + }, + (records) => { + if (records && records[0] && records[0].error) { + onFail(records[0].error); + } } ); -}; +} /** * Generic query builder for adding/removing grants/denies for a list of labels. @@ -120,6 +138,8 @@ export const retrieveAllowAndDenyLists = ( setLabels, setAllowList, setDenyList, + setFixedAllowList, + setFixedDenyList, setLoaded ) => { runCypherQuery( @@ -131,7 +151,7 @@ export const retrieveAllowAndDenyLists = ( AND role = $rolename AND action = 'match' AND segment STARTS WITH 'NODE(' - RETURN access, collect(substring(segment, 5, size(segment)-6)) as nodes`, + RETURN access, collect(substring(segment, 5, size(segment)-6)) as nodes, graph = "*" as fixed`, { rolename: currentRole, database: database }, 1000, (status) => { @@ -142,12 +162,21 @@ export const retrieveAllowAndDenyLists = ( }, (records) => { // Extract granted and denied label list from the result of the SHOW PRIVILEGES query - const grants = records.filter((r) => r._fields[0] == 'GRANTED'); - const denies = records.filter((r) => r._fields[0] == 'DENIED'); + const grants = records.filter((r) => r._fields[0] == 'GRANTED' && r._fields[2] == false); + const denies = records.filter((r) => r._fields[0] == 'DENIED' && r._fields[2] == false); const grantedLabels = grants[0] ? [...new Set(grants[0]._fields[1])] : []; const deniedLabels = denies[0] ? [...new Set(denies[0]._fields[1])] : []; - setAllowList(grantedLabels); - setDenyList(deniedLabels); + + // Do the same for fixed grants (those stored under the '*' graph permission) + const fixedGrants = records.filter((r) => r._fields[0] == 'GRANTED' && r._fields[2] == true); + const fixedDenies = records.filter((r) => r._fields[0] == 'DENIED' && r._fields[2] == true); + const fixedGrantedLabels = fixedGrants[0] ? [...new Set(fixedGrants[0]._fields[1])] : []; + const fixedDeniedLabels = fixedDenies[0] ? [...new Set(fixedDenies[0]._fields[1])] : []; + + setAllowList([...new Set(grantedLabels.concat(fixedGrantedLabels))]); + setDenyList([...new Set(deniedLabels.concat(fixedDeniedLabels))]); + setFixedAllowList(fixedGrantedLabels); + setFixedDenyList(fixedDeniedLabels); // Here we build a set of all POSSIBLE labels, that includes the list in the database, plus those in denies and grants. const possibleLabels = [...new Set(allLabels.concat(grantedLabels).concat(deniedLabels))]; @@ -226,8 +255,9 @@ export function retrieveDatabaseList(driver, setDatabases: React.Dispatch { +export async function updateUsers(driver, currentRole, allUsers, selectedUsers, onSuccess, onFail) { // 1. Build the query that removes all users from the role. + let globalStatus = -1; await runCypherQuery( driver, 'system', @@ -235,16 +265,32 @@ export const updateUsers = async (driver, currentRole, allUsers, selectedUsers) {}, 1000, (status) => { - if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) { - // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query. - // We build in an artificial delay... - const timeout = setTimeout(() => { - // 2. Re-assign only selected users to the role. - if (selectedUsers.length > 0) { - runCypherQuery(driver, 'system', `GRANT ROLE ${currentRole} TO ${selectedUsers.join(',')}`); - } - }, 1000); + globalStatus = status; + }, + (records) => { + if (records && records[0] && records[0].error) { + onFail(records[0].error); } } ); -}; + if (globalStatus == QueryStatus.NO_DATA || globalStatus == QueryStatus.COMPLETE) { + // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query. + // We build in an artificial delay... + if (selectedUsers.length > 0) { + await runCypherQuery( + driver, + 'system', + `GRANT ROLE ${currentRole} TO ${selectedUsers.join(',')}`, + {}, + 1000, + (status) => { + if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) { + onSuccess(); + } + } + ); + } else { + onSuccess(); + } + } +} diff --git a/src/modal/ExportModal.tsx b/src/modal/ExportModal.tsx index 7040ede4a..bb1d76fe0 100644 --- a/src/modal/ExportModal.tsx +++ b/src/modal/ExportModal.tsx @@ -14,7 +14,7 @@ export const NeoExportModal = ({ dashboard }) => { return ( <> - setOpen(true)} aria-label='Export' title='Export'> + setOpen(true)} aria-label='Export'> diff --git a/src/report/ReportQueryRunner.ts b/src/report/ReportQueryRunner.ts index 4259b66e6..a4cbc689b 100644 --- a/src/report/ReportQueryRunner.ts +++ b/src/report/ReportQueryRunner.ts @@ -65,6 +65,7 @@ export async function runCypherQuery( setStatus(QueryStatus.ERROR); return; } + console.log(query); const session = database ? driver.session({ database: database }) : driver.session(); const transaction = session.beginTransaction({ timeout: queryTimeLimit * 1000, connectionTimeout: 2000 }); From 4a1477271242172880df41856ace73738a4c511f Mon Sep 17 00:00:00 2001 From: Niels de Jong Date: Wed, 6 Mar 2024 11:59:10 +0100 Subject: [PATCH 08/26] Added 2.4.4 release notes, bumped version (#818) --- .github/workflows/master-deployment.yml | 4 +-- Dockerfile | 2 +- changelog.md | 9 +++++ .../pages/developer-guide/deploy-a-build.adoc | 2 +- package.json | 2 +- release-notes.md | 35 +++++-------------- src/modal/AboutModal.tsx | 2 +- 7 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.github/workflows/master-deployment.yml b/.github/workflows/master-deployment.yml index b170bcc03..a8a966f2a 100644 --- a/.github/workflows/master-deployment.yml +++ b/.github/workflows/master-deployment.yml @@ -79,7 +79,7 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.3 + tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.4 build-docker-legacy: needs: build-test runs-on: neodash-runners @@ -103,7 +103,7 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.3 + tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.4 deploy-gallery: runs-on: neodash-runners strategy: diff --git a/Dockerfile b/Dockerfile index c2f4fa361..a5c9fcf56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,4 +43,4 @@ USER nginx EXPOSE $NGINX_PORT HEALTHCHECK cmd curl --fail "http://localhost:$NGINX_PORT" || exit 1 -LABEL version="2.4.3" +LABEL version="2.4.4" diff --git a/changelog.md b/changelog.md index 1542e992d..c80e24157 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,12 @@ +## NeoDash 2.4.4 +This is a hotfix release fixing some breaking issues in the 2.4.3: +- Fixed number parsing using newer versions of the Neo4j driver. [811](https://github.com/neo4j-labs/neodash/pull/811) +- Reverted new connection handler for auto-renewed SSO sessions. [815](https://github.com/neo4j-labs/neodash/pull/815) +- Improved handling of parameters in form extension, resolved local state issues. [813](https://github.com/neo4j-labs/neodash/pull/813) +- Updated Role management extension to no longer execute queries in parallel, improved UX and error handling [813](https://github.com/neo4j-labs/neodash/pull/813) + +If you are currently using NeoDash version 2.4.3, we recommend updating as soon as possible. + ## NeoDash 2.4.3 This release contains several improvements and additions to multi-dashboard management, as well as a bug fixes and a variety of quality-of-life improvements: diff --git a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc index 8f25da594..d77d7e8b6 100644 --- a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc +++ b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc @@ -37,7 +37,7 @@ Depending on the webserver type and version, this could be different directory. As an example - to copy the files to an nginx webserver using `scp`: ```bash -scp neodash-2.4.3 username@host:/usr/share/nginx/html +scp neodash-2.4.4 username@host:/usr/share/nginx/html ``` NeoDash should now be visible by visiting your (sub)domain in the browser. diff --git a/package.json b/package.json index 1b3a74b3b..a5cc534df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neodash", - "version": "2.4.3", + "version": "2.4.4", "description": "NeoDash - Neo4j Dashboard Builder", "neo4jDesktop": { "apiVersion": "^1.2.0" diff --git a/release-notes.md b/release-notes.md index bfa9e6f9e..2b364ba45 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,27 +1,8 @@ -## NeoDash 2.4.3 -This release contains several improvements and additions to multi-dashboard management, as well as a bug fixes and a variety of quality-of-life improvements: - -Dashboard management and access control: -- Added a UI for handling dashboard access using RBAC, as well as a new extension to simply access control. -- Added button to sidebar to refresh the list of dashboards saved in the database. -- Improved handling and detection of draft dashboards in the dashboard sidebar. - -Other improvements: -- Changed CSV export functionality for tables to use UTF-8 format. -- Various improvements / fixes to the documentation to include new images, and up-to-date functionality. -- Added logic for handling refresh tokens when connected to NeoDash via SSO. -- Incorporated tooltips for bar charts with and without custom labels. - -Bug fixes and testing: -- Implemented bug fixes on type casting for numeric parameter selectors. -- Fixed issue with report actions not functioning properly on node click events. -- Extended test suite with Cypress tests for advanced settings in the bar chart. - -Thanks to all the contributors for this release: -- [OskarDamkjaer](https://github.com/OskarDamkjaer) -- [alfredorubin96](https://github.com/alfredorubin96), -- [AleSim94](https://github.com/AleSim94), -- [BennuFire](https://github.com/BennuFire), -- [jacobbleakley-neo4j](https://github.com/jacobbleakley-neo4j), -- [josepmonclus](https://github.com/josepmonclus) -- [nielsdejong](https://github.com/nielsdejong) \ No newline at end of file +## NeoDash 2.4.4 +This is a hotfix release fixing some breaking issues in the 2.4.3: +- Fixed number parsing using newer versions of the Neo4j driver. [811](https://github.com/neo4j-labs/neodash/pull/811) +- Reverted new connection handler for auto-renewed SSO sessions. [815](https://github.com/neo4j-labs/neodash/pull/815) +- Improved handling of parameters in form extension, resolved local state issues. [813](https://github.com/neo4j-labs/neodash/pull/813) +- Updated Role management extension to no longer execute queries in parallel, improved UX and error handling [813](https://github.com/neo4j-labs/neodash/pull/813) + +If you are currently using NeoDash version 2.4.3, we recommend updating as soon as possible. \ No newline at end of file diff --git a/src/modal/AboutModal.tsx b/src/modal/AboutModal.tsx index 1e40e9dab..d3e8d663f 100644 --- a/src/modal/AboutModal.tsx +++ b/src/modal/AboutModal.tsx @@ -3,7 +3,7 @@ import { Button, Dialog, TextLink } from '@neo4j-ndl/react'; import { BookOpenIconOutline, BeakerIconOutline } from '@neo4j-ndl/react/icons'; import { Section, SectionTitle, SectionContent } from './ModalUtils'; -export const version = '2.4.3'; +export const version = '2.4.4'; export const NeoAboutModal = ({ open, handleClose, getDebugState }) => { const downloadDebugFile = () => { From 7cd030242686e85c7dc5bc1c554a61e579c80fd0 Mon Sep 17 00:00:00 2001 From: Niels de Jong Date: Wed, 6 Mar 2024 12:03:11 +0100 Subject: [PATCH 09/26] Removed old console.log statements --- src/extensions/rbac/RBACManagementModal.tsx | 2 -- src/report/ReportQueryRunner.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/extensions/rbac/RBACManagementModal.tsx b/src/extensions/rbac/RBACManagementModal.tsx index 178fa081e..f3f17a08c 100644 --- a/src/extensions/rbac/RBACManagementModal.tsx +++ b/src/extensions/rbac/RBACManagementModal.tsx @@ -50,7 +50,6 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti }, [open]); useEffect(() => { - console.log([denyCompleted, allowCompleted, usersCompleted, failed]); if (failed !== false) { createNotification('Unable to update privileges', `${failed}`); } else if (denyCompleted && allowCompleted && usersCompleted) { @@ -121,7 +120,6 @@ export const RBACManagementModal = ({ open, handleClose, currentRole, createNoti setDenyCompleted(true); setAllowCompleted(true); } - console.log([denyCompleted, allowCompleted, usersCompleted, failed]); handleClose(); }; diff --git a/src/report/ReportQueryRunner.ts b/src/report/ReportQueryRunner.ts index a4cbc689b..599e4afde 100644 --- a/src/report/ReportQueryRunner.ts +++ b/src/report/ReportQueryRunner.ts @@ -65,7 +65,7 @@ export async function runCypherQuery( setStatus(QueryStatus.ERROR); return; } - console.log(query); + const session = database ? driver.session({ database: database }) : driver.session(); const transaction = session.beginTransaction({ timeout: queryTimeLimit * 1000, connectionTimeout: 2000 }); From 813e9d12ddf13b0eeb568ffff7bd3918042fdaf1 Mon Sep 17 00:00:00 2001 From: Niels de Jong Date: Fri, 15 Mar 2024 08:49:19 +0100 Subject: [PATCH 10/26] Update Dockerfile to use yarn.lock (#829) * 2.4.4 Release (#821) * fixing gantt chart, new driver version was returning {low,high} numbers * Revert "fixing gantt chart, new driver version was returning {low,high} numbers" This reverts commit 4eb92856dd947bc5b768d17bf1a44eab99f2148a. * fixing gantt chart, new driver version was returning {low,high} numbers * NaNs * fixing number parsing * Revert "Handle token expiration in SSO (#611)" (#815) This reverts commit 001cee1cfe80a21f7d348cea03a74e1328cbe5df. * Fixing RBAC feature + parameters (#813) * fixed race condition, to work on other points in PR * Fixes for complex parameter types in forms * Added special case for handling cross-db label access * handling fixed grants without non-fixed grants * Added error handling to RBAC extension * Added back async modifier * bug fixin grbac --------- Co-authored-by: Alfred Rubin Co-authored-by: Niels de Jong * Added 2.4.4 release notes, bumped version (#818) * Removed old console.log statements --------- Co-authored-by: Alfred Rubin Co-authored-by: alfredorubin96 <103421036+alfredorubin96@users.noreply.github.com> Co-authored-by: Harold Agudelo * Update Dockerfile to use yarn.lock --------- Co-authored-by: Alfred Rubin Co-authored-by: alfredorubin96 <103421036+alfredorubin96@users.noreply.github.com> Co-authored-by: Harold Agudelo --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index a5c9fcf56..5ba5230bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ WORKDIR /usr/local/src/neodash # Copy sources and install/build COPY ./package.json /usr/local/src/neodash/package.json +COPY ./yarn.lock /usr/local/src/neodash/yarn.lock RUN yarn install COPY ./ /usr/local/src/neodash From ab5e2f79fa4ae7d6ef2c123a1c233814bb7cf359 Mon Sep 17 00:00:00 2001 From: LiamEdwardsLamarche Date: Tue, 9 Apr 2024 10:47:10 +0100 Subject: [PATCH 11/26] fixes bug where arrays are passed as [object Object] --- src/chart/ChartUtils.ts | 10 ++++++++-- src/chart/table/TableChart.tsx | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/chart/ChartUtils.ts b/src/chart/ChartUtils.ts index dd7eaf6b4..1ff72b7f6 100644 --- a/src/chart/ChartUtils.ts +++ b/src/chart/ChartUtils.ts @@ -218,8 +218,14 @@ export function replaceDashboardParameters(str, parameters) { let param = _.replace(`$`, '').trim(); let val = parameters?.[param] || null; let type = getRecordType(val); - let valueRender = type === 'string' || type == 'link' ? val : RenderSubValue(val); - return valueRender; + + // Arrays weren't playing nicely with RenderSubValue(). Each object would be passed separately and return [oject Object]. + if (type === 'string' || type == 'link' ) { + return val; + } else if (type === 'array') { + return RenderSubValue(val.join(', ')); + } + return RenderSubValue(val); }; let newString = str.replace(rx, parameterElementReplacer).replace(rxSimple, parameterSimpleReplacer); diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index 7762d526b..6da5f398d 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -50,7 +50,7 @@ function renderAsButtonWrapper(renderer) { style={{ width: '100%', marginLeft: '5px', marginRight: '5px' }} variant='contained' color='primary' - >{`${outputValue}`} + >{outputValue} ); }; } From 88dbcc73170b7c9c4ee428fcb10a138de405211d Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Tue, 9 Apr 2024 15:03:55 +0200 Subject: [PATCH 12/26] Wrap text in tables --- public/style.css | 42 +++++++++++++++------------------- src/chart/table/TableChart.tsx | 8 ++++++- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/public/style.css b/public/style.css index 5896897bf..63cf9fca1 100644 --- a/public/style.css +++ b/public/style.css @@ -125,17 +125,13 @@ margin-top: -10px; } -.MuiDataGrid-virtualScroller { - overflow-y: hidden !important; -} - -.MuiDataGrid-panel{ +.MuiDataGrid-panel { translate: 0px -152%; } .MuiCard-root { - box-shadow: 0 0 #0000,0 0 #0000,var(--tw-shadow) !important; - box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow) !important; + box-shadow: 0 0 #0000, 0 0 #0000, var(--tw-shadow) !important; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important; } .white-text { @@ -188,7 +184,6 @@ text-align: center; } - .card-view.expanded { position: absolute; top: 0; @@ -218,24 +213,23 @@ .card-view .MuiTablePagination-root { margin-top: 0px; - } @keyframes pulse { - 0% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7); - } + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7); + } - 70% { - transform: scale(1); - box-shadow: 0 0 0 10px rgba(0, 0, 0, 0); - } + 70% { + transform: scale(1); + box-shadow: 0 0 0 10px rgba(0, 0, 0, 0); + } - 100% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); - } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); + } } /* Workaround for Needle not handling menu placement of dropdowns on modals */ @@ -247,14 +241,14 @@ /* End workaround */ /* Workaround for cleaning the Gantt chart UI */ -.gantt-wrapper > div > div:first-child > div:first-child > div:first-child > div> div:not(:first-child) { +.gantt-wrapper > div > div:first-child > div:first-child > div:first-child > div > div:not(:first-child) { display: none; } -.gantt-wrapper > div > div > div > div> div> div> div:not(:first-child) { +.gantt-wrapper > div > div > div > div > div > div > div:not(:first-child) { display: none; } /* End Gantt chart workaround */ .markdown-widget a { text-decoration: underline; -} \ No newline at end of file +} diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index 7762d526b..b57d19ffe 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -262,7 +262,8 @@ export const NeoTableChart = (props: ChartProps) => { 'auto'} rows={rows} columns={columns} columnVisibilityModel={columnVisibilityModel} @@ -305,6 +306,11 @@ export const NeoTableChart = (props: ChartProps) => { }) .join(' '); }} + sx={{ + '&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell': { py: '3px' }, + '&.MuiDataGrid-root--densityStandard .MuiDataGrid-cell': { py: '15px' }, + '&.MuiDataGrid-root--densityComfortable .MuiDataGrid-cell': { py: '22px' }, + }} /> From d5d78df3560f9342747c002bd9ba7bea16fb7d62 Mon Sep 17 00:00:00 2001 From: AleSim94 Date: Tue, 9 Apr 2024 15:30:00 +0200 Subject: [PATCH 13/26] added new scrollable bar(inline style) + more robust way to update users with special chars in their name like for example "@" and "<". --- src/extensions/rbac/RBACManagementMenu.tsx | 2 +- src/extensions/rbac/RBACUtils.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx index 1a85b08fe..b5c20c0b3 100644 --- a/src/extensions/rbac/RBACManagementMenu.tsx +++ b/src/extensions/rbac/RBACManagementMenu.tsx @@ -64,7 +64,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti onClose={handleClose} size='small' > - + {roles.map((role) => ( handleRoleClicked(role)} icon={} title={role} /> ))} diff --git a/src/extensions/rbac/RBACUtils.ts b/src/extensions/rbac/RBACUtils.ts index 69e701bb9..c93d54e56 100644 --- a/src/extensions/rbac/RBACUtils.ts +++ b/src/extensions/rbac/RBACUtils.ts @@ -258,10 +258,11 @@ export function retrieveDatabaseList(driver, setDatabases: React.Dispatch `\`${user}\``).join(','); await runCypherQuery( driver, 'system', - `REVOKE ROLE ${currentRole} FROM ${allUsers.join(',')}`, + `REVOKE ROLE ${currentRole} FROM ${escapedAllUsers}`, {}, 1000, (status) => { @@ -277,10 +278,11 @@ export async function updateUsers(driver, currentRole, allUsers, selectedUsers, // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query. // We build in an artificial delay... if (selectedUsers.length > 0) { + const escapedSelectedUsers = selectedUsers.map((user) => `\`${user}\``).join(','); await runCypherQuery( driver, 'system', - `GRANT ROLE ${currentRole} TO ${selectedUsers.join(',')}`, + `GRANT ROLE ${currentRole} TO ${escapedSelectedUsers}`, {}, 1000, (status) => { From ea97143d8928efeca050fc95a6ede5aa12571184 Mon Sep 17 00:00:00 2001 From: AleSim94 Date: Tue, 9 Apr 2024 18:05:51 +0200 Subject: [PATCH 14/26] chnaged the new scollable bar from inline styling to tailwind styling --- src/extensions/rbac/RBACManagementMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx index b5c20c0b3..ed8f6e49e 100644 --- a/src/extensions/rbac/RBACManagementMenu.tsx +++ b/src/extensions/rbac/RBACManagementMenu.tsx @@ -64,7 +64,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti onClose={handleClose} size='small' > - + {roles.map((role) => ( handleRoleClicked(role)} icon={} title={role} /> ))} From 774dc7c138781ef3cc772b162965b1ab0a7508bb Mon Sep 17 00:00:00 2001 From: AleSim94 Date: Tue, 9 Apr 2024 18:11:51 +0200 Subject: [PATCH 15/26] just made it little bigger --- src/extensions/rbac/RBACManagementMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx index ed8f6e49e..b37320098 100644 --- a/src/extensions/rbac/RBACManagementMenu.tsx +++ b/src/extensions/rbac/RBACManagementMenu.tsx @@ -64,7 +64,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti onClose={handleClose} size='small' > - + {roles.map((role) => ( handleRoleClicked(role)} icon={} title={role} /> ))} From 52e3438a0933bf35c57e641a8537436521d8d72e Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Wed, 10 Apr 2024 09:10:28 +0200 Subject: [PATCH 16/26] Wrap words --- src/chart/table/TableChart.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index b57d19ffe..2cebb9cdb 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -310,6 +310,7 @@ export const NeoTableChart = (props: ChartProps) => { '&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell': { py: '3px' }, '&.MuiDataGrid-root--densityStandard .MuiDataGrid-cell': { py: '15px' }, '&.MuiDataGrid-root--densityComfortable .MuiDataGrid-cell': { py: '22px' }, + '&.MuiDataGrid-root .MuiDataGrid-cell': { wordBreak: 'break-word' }, }} /> From cafc366103e0e9ccdbf555a5717891584279545b Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Wed, 10 Apr 2024 11:51:21 +0200 Subject: [PATCH 17/26] Render HTML strings --- package.json | 1 + .../graph/component/GraphEntityInspectionTable.tsx | 13 ++++++++----- src/chart/table/TableChart.tsx | 12 +++++++++++- src/report/ReportRecordProcessing.tsx | 4 +++- yarn.lock | 5 +++++ 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a5cc534df..13d16d0f2 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "d3-scale-chromatic": "^3.0.0", "dayjs": "^1.11.7", "dom-to-image": "^2.6.0", + "dompurify": "^3.1.0", "leaflet": "^1.7.1", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx index 62801204f..0349f102e 100644 --- a/src/chart/graph/component/GraphEntityInspectionTable.tsx +++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx @@ -2,16 +2,19 @@ import React from 'react'; import ShowMoreText from 'react-show-more-text'; import { Checkbox, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; import { TextLink } from '@neo4j-ndl/react'; +import DOMPurify from 'dompurify'; export const formatProperty = (property) => { - if (property.startsWith('http://') || property.startsWith('https://')) { + const str = property?.toString() || ''; + if (str.startsWith('http://') || str.startsWith('https://')) { return ( - - {property} + + {str} ); } - return property; + const cleanValue = DOMPurify.sanitize(str); + return
; }; /** @@ -88,7 +91,7 @@ export const GraphEntityInspectionTable = ({ {key} - {formatProperty(entity && entity.properties[key].toString())} + {formatProperty(entity && entity.properties[key])} {checklistEnabled ? ( diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index 7762d526b..61d2ac9c2 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -23,6 +23,7 @@ import { ThemeProvider, createTheme } from '@mui/material/styles'; import Button from '@mui/material/Button'; import { extensionEnabled } from '../../utils/ReportUtils'; import { getCheckboxes, hasCheckboxes, updateCheckBoxes } from './TableActionsHelper'; +import DOMPurify from 'dompurify'; const TABLE_HEADER_HEIGHT = 32; const TABLE_FOOTER_HEIGHT = 62; @@ -38,6 +39,15 @@ const fallbackRenderer = (value) => { return JSON.stringify(value); }; +function htmlToPlainText(html): string { + // Create a temporary div element to hold the sanitized HTML content + const tempElement = document.createElement('div'); + // Set the HTML content directly as innerHTML of the temporary element + tempElement.innerHTML = html.props.dangerouslySetInnerHTML.__html; + // Extract plain text using textContent + return tempElement.textContent || ''; +} + function renderAsButtonWrapper(renderer) { return function renderAsButton(value) { const outputValue = renderer(value, true); @@ -50,7 +60,7 @@ function renderAsButtonWrapper(renderer) { style={{ width: '100%', marginLeft: '5px', marginRight: '5px' }} variant='contained' color='primary' - >{`${outputValue}`} + >{`${htmlToPlainText(outputValue)}`} ); }; } diff --git a/src/report/ReportRecordProcessing.tsx b/src/report/ReportRecordProcessing.tsx index 1109d1e3a..3c5ac01ee 100644 --- a/src/report/ReportRecordProcessing.tsx +++ b/src/report/ReportRecordProcessing.tsx @@ -10,6 +10,7 @@ import { valueIsPath, valueIsRelationship, } from '../chart/ChartUtils'; +import DOMPurify from 'dompurify'; /** * Collects all node labels and node properties in a set of Neo4j records. @@ -266,7 +267,8 @@ function RenderString(value) { ); } - return str; + const cleanValue = DOMPurify.sanitize(str); + return
; } function RenderLink(value, disabled = false) { diff --git a/yarn.lock b/yarn.lock index d9935f9e9..2e254bea7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7068,6 +7068,11 @@ dom-to-image@^2.6.0: resolved "https://registry.yarnpkg.com/dom-to-image/-/dom-to-image-2.6.0.tgz#8a503608088c87b1c22f9034ae032e1898955867" integrity sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA== +dompurify@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.0.tgz#8c6b9fe986969a33aa4686bd829cbe8e14dd9445" + integrity sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA== + dotenv@^16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" From 0d24ec0c3399358b8d41653bd68853ab7af632ce Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Wed, 10 Apr 2024 12:16:31 +0200 Subject: [PATCH 18/26] Disable auto-sorting for Cypher Parameter Select --- src/chart/parameter/ParameterSelectionChart.tsx | 3 +++ .../parameter/component/NodePropertyParameterSelect.tsx | 7 +++++-- src/chart/parameter/component/ParameterSelect.ts | 4 ++++ src/chart/parameter/component/QueryParameterSelect.tsx | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/chart/parameter/ParameterSelectionChart.tsx b/src/chart/parameter/ParameterSelectionChart.tsx index bb48334b0..56a67ea09 100644 --- a/src/chart/parameter/ParameterSelectionChart.tsx +++ b/src/chart/parameter/ParameterSelectionChart.tsx @@ -94,6 +94,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => { compatibilityMode={compatibilityMode} multiSelector={multiSelector} manualParameterSave={manualParameterSave} + autoSort={true} /> ); } else if (type == 'Relationship Property') { @@ -112,6 +113,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => { compatibilityMode={compatibilityMode} multiSelector={multiSelector} manualParameterSave={manualParameterSave} + autoSort={true} /> ); } else if (type == 'Date Picker') { @@ -147,6 +149,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => { compatibilityMode={compatibilityMode} multiSelector={multiSelector} manualParameterSave={manualParameterSave} + autoSort={false} /> ); } diff --git a/src/chart/parameter/component/NodePropertyParameterSelect.tsx b/src/chart/parameter/component/NodePropertyParameterSelect.tsx index 29d79ae28..4972ff225 100644 --- a/src/chart/parameter/component/NodePropertyParameterSelect.tsx +++ b/src/chart/parameter/component/NodePropertyParameterSelect.tsx @@ -165,12 +165,14 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => { /> ); } + let options = extraRecords?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)'); + options = props.autoSort ? options.sort() : options; return (
r?._fields?.[displayValueRowIndex] || '(no data)').sort()} + options={options} disabled={disabled} limitTags={multiSelectLimit} style={{ @@ -198,7 +200,8 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => { if (autoSelectFirstValue && paramValueDisplayLocal == '') { debouncedQueryCallback(props.query, { input: '', ...allParameters }, (records) => { if (records && records.length > 0 && records[0] && records[0]._fields) { - const values = records?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)').sort(); + let values = records?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)'); + values = props.autoSort ? values.sort() : values; setExtraRecords(records); propagateSelection(undefined, values[0]); } diff --git a/src/chart/parameter/component/ParameterSelect.ts b/src/chart/parameter/component/ParameterSelect.ts index 2d9cf9966..54e9f2a67 100644 --- a/src/chart/parameter/component/ParameterSelect.ts +++ b/src/chart/parameter/component/ParameterSelect.ts @@ -62,4 +62,8 @@ export interface ParameterSelectProps { * Add the possibility for manual selection confirmation */ manualParameterSave?: boolean; + /** + * Pass true if results should be sorted automatically + */ + autoSort?: boolean; } diff --git a/src/chart/parameter/component/QueryParameterSelect.tsx b/src/chart/parameter/component/QueryParameterSelect.tsx index e4fdeb807..125c10532 100644 --- a/src/chart/parameter/component/QueryParameterSelect.tsx +++ b/src/chart/parameter/component/QueryParameterSelect.tsx @@ -17,6 +17,7 @@ const QueryParameterSelectComponent = (props: ParameterSelectProps) => { allParameters={props.allParameters} compatibilityMode={props.compatibilityMode} multiSelector={props.multiSelector} + autoSort={props.autoSort} /> ); }; From 6416b0672c6feea33d1ca57f20663a8ebfd8c3b6 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Thu, 11 Apr 2024 12:39:25 +0200 Subject: [PATCH 19/26] cleaning according to sonarqube --- src/chart/graph/component/GraphEntityInspectionTable.tsx | 2 +- src/chart/table/TableChart.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx index 0349f102e..6aee24f3c 100644 --- a/src/chart/graph/component/GraphEntityInspectionTable.tsx +++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx @@ -91,7 +91,7 @@ export const GraphEntityInspectionTable = ({ {key} - {formatProperty(entity && entity.properties[key])} + {formatProperty(entity?.properties[key])} {checklistEnabled ? ( diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index 61d2ac9c2..ecbf9c39b 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -23,7 +23,6 @@ import { ThemeProvider, createTheme } from '@mui/material/styles'; import Button from '@mui/material/Button'; import { extensionEnabled } from '../../utils/ReportUtils'; import { getCheckboxes, hasCheckboxes, updateCheckBoxes } from './TableActionsHelper'; -import DOMPurify from 'dompurify'; const TABLE_HEADER_HEIGHT = 32; const TABLE_FOOTER_HEIGHT = 62; @@ -45,7 +44,7 @@ function htmlToPlainText(html): string { // Set the HTML content directly as innerHTML of the temporary element tempElement.innerHTML = html.props.dangerouslySetInnerHTML.__html; // Extract plain text using textContent - return tempElement.textContent || ''; + return tempElement.textContent ?? ''; } function renderAsButtonWrapper(renderer) { From ec61cb1762ba55b6753618b8fc2b12b499c396a0 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Thu, 11 Apr 2024 13:17:23 +0200 Subject: [PATCH 20/26] shrinking menu lenght --- src/extensions/rbac/RBACManagementMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx index b37320098..31ec37f81 100644 --- a/src/extensions/rbac/RBACManagementMenu.tsx +++ b/src/extensions/rbac/RBACManagementMenu.tsx @@ -64,7 +64,7 @@ export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNoti onClose={handleClose} size='small' > - + {roles.map((role) => ( handleRoleClicked(role)} icon={} title={role} /> ))} From 28cc5a61b7c45f4d369099679636b33ba6c7bf47 Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Mon, 22 Apr 2024 10:14:58 +0200 Subject: [PATCH 21/26] Fix padding in compact table with report actions --- src/chart/table/TableChart.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index cd5ade78c..ea055cedd 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -317,6 +317,7 @@ export const NeoTableChart = (props: ChartProps) => { }} sx={{ '&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell': { py: '3px' }, + '&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell:has(button)': { py: '0px' }, '&.MuiDataGrid-root--densityStandard .MuiDataGrid-cell': { py: '15px' }, '&.MuiDataGrid-root--densityComfortable .MuiDataGrid-cell': { py: '22px' }, '&.MuiDataGrid-root .MuiDataGrid-cell': { wordBreak: 'break-word' }, From ae7e3e59c6b3c881b81a2bc005bb67ca078eedd9 Mon Sep 17 00:00:00 2001 From: LiamEdwardsLamarche Date: Mon, 22 Apr 2024 09:38:54 +0100 Subject: [PATCH 22/26] html render rollback due to too many bugs --- .../component/GraphEntityInspectionTable.tsx | 12 ++++++++---- src/chart/table/TableChart.tsx | 19 ++++++++++--------- src/report/ReportRecordProcessing.tsx | 8 +++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx index 6aee24f3c..dd21e8182 100644 --- a/src/chart/graph/component/GraphEntityInspectionTable.tsx +++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; import ShowMoreText from 'react-show-more-text'; import { Checkbox, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; import { TextLink } from '@neo4j-ndl/react'; -import DOMPurify from 'dompurify'; +// import DOMPurify from 'dompurify'; export const formatProperty = (property) => { const str = property?.toString() || ''; @@ -13,8 +13,11 @@ export const formatProperty = (property) => { ); } - const cleanValue = DOMPurify.sanitize(str); - return
; + return property; + + // html render rollback + // const cleanValue = DOMPurify.sanitize(str); + // return
; }; /** @@ -91,7 +94,8 @@ export const GraphEntityInspectionTable = ({ {key} - {formatProperty(entity?.properties[key])} + {formatProperty(entity && entity.properties[key].toString())} + {/* {formatProperty(entity?.properties[key])} */} {checklistEnabled ? ( diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index cd5ade78c..c25acfe0a 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -38,14 +38,15 @@ const fallbackRenderer = (value) => { return JSON.stringify(value); }; -function htmlToPlainText(html): string { - // Create a temporary div element to hold the sanitized HTML content - const tempElement = document.createElement('div'); - // Set the HTML content directly as innerHTML of the temporary element - tempElement.innerHTML = html.props.dangerouslySetInnerHTML.__html; - // Extract plain text using textContent - return tempElement.textContent ?? ''; -} +// html Render Rollback +// function htmlToPlainText(html): string { +// // Create a temporary div element to hold the sanitized HTML content +// const tempElement = document.createElement('div'); +// // Set the HTML content directly as innerHTML of the temporary element +// tempElement.innerHTML = html.props.dangerouslySetInnerHTML.__html; +// // Extract plain text using textContent +// return tempElement.textContent ?? ''; +// } function renderAsButtonWrapper(renderer) { return function renderAsButton(value) { @@ -59,7 +60,7 @@ function renderAsButtonWrapper(renderer) { style={{ width: '100%', marginLeft: '5px', marginRight: '5px' }} variant='contained' color='primary' - >{`${htmlToPlainText(outputValue)}`} + >{outputValue} ); }; } diff --git a/src/report/ReportRecordProcessing.tsx b/src/report/ReportRecordProcessing.tsx index 3c5ac01ee..5d797cd4f 100644 --- a/src/report/ReportRecordProcessing.tsx +++ b/src/report/ReportRecordProcessing.tsx @@ -10,7 +10,7 @@ import { valueIsPath, valueIsRelationship, } from '../chart/ChartUtils'; -import DOMPurify from 'dompurify'; +// import DOMPurify from 'dompurify'; /** * Collects all node labels and node properties in a set of Neo4j records. @@ -267,8 +267,10 @@ function RenderString(value) { ); } - const cleanValue = DOMPurify.sanitize(str); - return
; + return str; + // html render rollback + // const cleanValue = DOMPurify.sanitize(str); + // return
; } function RenderLink(value, disabled = false) { From a2814068805297062cedac88c331a1bc7f0f878b Mon Sep 17 00:00:00 2001 From: LiamEdwardsLamarche Date: Mon, 22 Apr 2024 09:52:38 +0100 Subject: [PATCH 23/26] fixes typo --- src/chart/graph/component/GraphEntityInspectionTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx index dd21e8182..eefb6f90d 100644 --- a/src/chart/graph/component/GraphEntityInspectionTable.tsx +++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx @@ -13,7 +13,7 @@ export const formatProperty = (property) => { ); } - return property; + return str; // html render rollback // const cleanValue = DOMPurify.sanitize(str); From 62cf070fcb417a0fde58ec86e1dc77ab1f80be92 Mon Sep 17 00:00:00 2001 From: LiamEdwardsLamarche Date: Mon, 22 Apr 2024 10:08:31 +0100 Subject: [PATCH 24/26] code cleanup --- .../graph/component/GraphEntityInspectionTable.tsx | 7 +------ src/chart/table/TableChart.tsx | 10 ---------- src/report/ReportRecordProcessing.tsx | 3 --- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx index eefb6f90d..562fbbaec 100644 --- a/src/chart/graph/component/GraphEntityInspectionTable.tsx +++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx @@ -14,10 +14,6 @@ export const formatProperty = (property) => { ); } return str; - - // html render rollback - // const cleanValue = DOMPurify.sanitize(str); - // return
; }; /** @@ -94,8 +90,7 @@ export const GraphEntityInspectionTable = ({ {key} - {formatProperty(entity && entity.properties[key].toString())} - {/* {formatProperty(entity?.properties[key])} */} + {formatProperty(entity?.properties[key])} {checklistEnabled ? ( diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx index ab3ba822c..c6bdf3637 100644 --- a/src/chart/table/TableChart.tsx +++ b/src/chart/table/TableChart.tsx @@ -38,16 +38,6 @@ const fallbackRenderer = (value) => { return JSON.stringify(value); }; -// html Render Rollback -// function htmlToPlainText(html): string { -// // Create a temporary div element to hold the sanitized HTML content -// const tempElement = document.createElement('div'); -// // Set the HTML content directly as innerHTML of the temporary element -// tempElement.innerHTML = html.props.dangerouslySetInnerHTML.__html; -// // Extract plain text using textContent -// return tempElement.textContent ?? ''; -// } - function renderAsButtonWrapper(renderer) { return function renderAsButton(value) { const outputValue = renderer(value, true); diff --git a/src/report/ReportRecordProcessing.tsx b/src/report/ReportRecordProcessing.tsx index 5d797cd4f..726beaebe 100644 --- a/src/report/ReportRecordProcessing.tsx +++ b/src/report/ReportRecordProcessing.tsx @@ -268,9 +268,6 @@ function RenderString(value) { ); } return str; - // html render rollback - // const cleanValue = DOMPurify.sanitize(str); - // return
; } function RenderLink(value, disabled = false) { From eb05802d79416f9bd8a67bbaec640ce20a5e38e8 Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Mon, 22 Apr 2024 17:11:44 +0200 Subject: [PATCH 25/26] Update release notes 2.4.5 --- release-notes.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/release-notes.md b/release-notes.md index 2b364ba45..2c8fb27d3 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,8 +1,11 @@ -## NeoDash 2.4.4 -This is a hotfix release fixing some breaking issues in the 2.4.3: -- Fixed number parsing using newer versions of the Neo4j driver. [811](https://github.com/neo4j-labs/neodash/pull/811) -- Reverted new connection handler for auto-renewed SSO sessions. [815](https://github.com/neo4j-labs/neodash/pull/815) -- Improved handling of parameters in form extension, resolved local state issues. [813](https://github.com/neo4j-labs/neodash/pull/813) -- Updated Role management extension to no longer execute queries in parallel, improved UX and error handling [813](https://github.com/neo4j-labs/neodash/pull/813) +## NeoDash 2.4.5 +This is a small release containing a few fixes: +- Fixed rendering of string arrays inside tables, report titles, and report action buttons [849](https://github.com/neo4j-labs/neodash/pull/849) +- Allowed text to wrap in tables, preserving the number of rows [852](https://github.com/neo4j-labs/neodash/pull/852) +- Disabled auto-sorting of Cypher query-based Parameter Select ; use Cypher ORDER BY to control result order [857](https://github.com/neo4j-labs/neodash/pull/857) +- Updated role selector menu, and made user updates more robust [854](https://github.com/neo4j-labs/neodash/pull/854) -If you are currently using NeoDash version 2.4.3, we recommend updating as soon as possible. \ No newline at end of file +Thanks to all the contributors for this release: +- [MariusC](https://github.com/mariusconjeaud), +- [LiamEdwardsLamarche](https://github.com/LiamEdwardsLamarche), +- [AleSim94](https://github.com/AleSim94) \ No newline at end of file From 2adc6e873b6ca18ce55e357aee0532fc82931d20 Mon Sep 17 00:00:00 2001 From: Marius Conjeaud Date: Mon, 22 Apr 2024 17:41:34 +0200 Subject: [PATCH 26/26] Update version tags --- .github/workflows/master-deployment.yml | 6 +++--- Dockerfile | 2 +- changelog.md | 12 ++++++++++++ .../ROOT/pages/developer-guide/deploy-a-build.adoc | 2 +- package.json | 2 +- src/modal/AboutModal.tsx | 2 +- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/master-deployment.yml b/.github/workflows/master-deployment.yml index a8a966f2a..8838807ac 100644 --- a/.github/workflows/master-deployment.yml +++ b/.github/workflows/master-deployment.yml @@ -31,7 +31,7 @@ jobs: with: build: yarn run build start: yarn run dev - wait-on: "http://localhost:3000" + wait-on: 'http://localhost:3000' browser: chrome build-s3: needs: build-test @@ -79,7 +79,7 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.4 + tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.5 build-docker-legacy: needs: build-test runs-on: neodash-runners @@ -103,7 +103,7 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.4 + tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.5 deploy-gallery: runs-on: neodash-runners strategy: diff --git a/Dockerfile b/Dockerfile index 5ba5230bd..0bbdf284b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,4 +44,4 @@ USER nginx EXPOSE $NGINX_PORT HEALTHCHECK cmd curl --fail "http://localhost:$NGINX_PORT" || exit 1 -LABEL version="2.4.4" +LABEL version="2.4.5" diff --git a/changelog.md b/changelog.md index c80e24157..b34b6d2bd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,15 @@ +## NeoDash 2.4.5 +This is a small release containing a few fixes: +- Fixed rendering of string arrays inside tables, report titles, and report action buttons [849](https://github.com/neo4j-labs/neodash/pull/849) +- Allowed text to wrap in tables, preserving the number of rows [852](https://github.com/neo4j-labs/neodash/pull/852) +- Disabled auto-sorting of Cypher query-based Parameter Select ; use Cypher ORDER BY to control result order [857](https://github.com/neo4j-labs/neodash/pull/857) +- Updated role selector menu, and made user updates more robust [854](https://github.com/neo4j-labs/neodash/pull/854) + +Thanks to all the contributors for this release: +- [MariusC](https://github.com/mariusconjeaud), +- [LiamEdwardsLamarche](https://github.com/LiamEdwardsLamarche), +- [AleSim94](https://github.com/AleSim94) + ## NeoDash 2.4.4 This is a hotfix release fixing some breaking issues in the 2.4.3: - Fixed number parsing using newer versions of the Neo4j driver. [811](https://github.com/neo4j-labs/neodash/pull/811) diff --git a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc index d77d7e8b6..1b7d4a371 100644 --- a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc +++ b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc @@ -37,7 +37,7 @@ Depending on the webserver type and version, this could be different directory. As an example - to copy the files to an nginx webserver using `scp`: ```bash -scp neodash-2.4.4 username@host:/usr/share/nginx/html +scp neodash-2.4.5 username@host:/usr/share/nginx/html ``` NeoDash should now be visible by visiting your (sub)domain in the browser. diff --git a/package.json b/package.json index 13d16d0f2..f15e63413 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neodash", - "version": "2.4.4", + "version": "2.4.5", "description": "NeoDash - Neo4j Dashboard Builder", "neo4jDesktop": { "apiVersion": "^1.2.0" diff --git a/src/modal/AboutModal.tsx b/src/modal/AboutModal.tsx index d3e8d663f..8c1be47e5 100644 --- a/src/modal/AboutModal.tsx +++ b/src/modal/AboutModal.tsx @@ -3,7 +3,7 @@ import { Button, Dialog, TextLink } from '@neo4j-ndl/react'; import { BookOpenIconOutline, BeakerIconOutline } from '@neo4j-ndl/react/icons'; import { Section, SectionTitle, SectionContent } from './ModalUtils'; -export const version = '2.4.4'; +export const version = '2.4.5'; export const NeoAboutModal = ({ open, handleClose, getDebugState }) => { const downloadDebugFile = () => {