From dedb0d9722d6c47e9eed123e9072f76f48e80378 Mon Sep 17 00:00:00 2001
From: Elmahdi ABBASSI <108519266+emabassi-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 09:01:29 +0100
Subject: [PATCH 01/18] [SNYK] Sanitize and bind ACL host dependency queries
(#11389) (#11394)
* Sanitize and bind ACL host dependency queries
* fix issues
---
.../configObject/host_dependency/DB-Func.php | 27 ++++++++++++-------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/www/include/configuration/configObject/host_dependency/DB-Func.php b/www/include/configuration/configObject/host_dependency/DB-Func.php
index a7b09cbeeaf..57209f88c74 100644
--- a/www/include/configuration/configObject/host_dependency/DB-Func.php
+++ b/www/include/configuration/configObject/host_dependency/DB-Func.php
@@ -123,11 +123,14 @@ function multipleHostDependencyInDB($dependencies = array(), $nbrDup = array())
"WHERE dependency_dep_id = " . $key;
$dbResult = $pearDB->query($query);
$fields["dep_serviceChilds"] = "";
+ $statement = $pearDB->prepare("INSERT INTO dependency_serviceChild_relation " .
+ " VALUES (:max_dep_id, :service_id, :host_host_id)");
while ($service = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_serviceChild_relation VALUES ('" .
- $maxId["MAX(dep_id)"] . "', '" . $service["service_service_id"] . "', '" .
- $service["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':max_dep_id', (int)$maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':service_id', (int)$service["service_service_id"], \PDO::PARAM_INT);
+ $statement->bindValue(':host_host_id', (int)$service["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
+
$fields["dep_serviceChilds"] .= $service["host_host_id"] .
'-' . $service["service_service_id"] . ",";
}
@@ -136,10 +139,12 @@ function multipleHostDependencyInDB($dependencies = array(), $nbrDup = array())
"WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hostParents"] = "";
+ $statement = $pearDB->prepare("INSERT INTO dependency_hostParent_relation " .
+ "VALUES (:max_dep_id, :host_host_id)");
while ($host = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_hostParent_relation " .
- "VALUES ('" . $maxId["MAX(dep_id)"] . "', '" . $host["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':max_dep_id', (int)$maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':host_host_id', (int)$host["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hostParents"] .= $host["host_host_id"] . ",";
}
$fields["dep_hostParents"] = trim($fields["dep_hostParents"], ",");
@@ -148,10 +153,12 @@ function multipleHostDependencyInDB($dependencies = array(), $nbrDup = array())
"WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hostChilds"] = "";
+ $statement = $pearDB->prepare("INSERT INTO dependency_hostChild_relation " .
+ "VALUES (:max_dep_id, :host_host_id)");
while ($host = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_hostChild_relation " .
- "VALUES ('" . $maxId["MAX(dep_id)"] . "', '" . $host["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':max_dep_id', (int)$maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':host_host_id', (int)$host["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hostChilds"] .= $host["host_host_id"] . ",";
}
$fields["dep_hostChilds"] = trim($fields["dep_hostChilds"], ",");
From 1756cf1f95cd5b06454f5542ec697ad035015d12 Mon Sep 17 00:00:00 2001
From: Elmahdi ABBASSI <108519266+emabassi-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 09:03:35 +0100
Subject: [PATCH 02/18] removed old variable userCrypted and the use of it
(#11334) (#11352)
Co-authored-by: jeremyjaouen <61694165+jeremyjaouen@users.noreply.github.com>
---
www/class/centreonUser.class.php | 2 --
1 file changed, 2 deletions(-)
diff --git a/www/class/centreonUser.class.php b/www/class/centreonUser.class.php
index 2c61e80a732..398ed4733f2 100644
--- a/www/class/centreonUser.class.php
+++ b/www/class/centreonUser.class.php
@@ -56,7 +56,6 @@ class CentreonUser
public $groupListStr;
public $access;
public $log;
- public $userCrypted;
protected $token;
public $default_page;
private $showDeprecatedPages;
@@ -109,7 +108,6 @@ public function __construct($user = array())
* Initiate Log Class
*/
$this->log = new CentreonUserLog($this->user_id, $pearDB);
- $this->userCrypted = md5($this->alias);
/**
* Init rest api auth
From 598c0012e93203fe64ccf83a82fb8d83c5ae2435 Mon Sep 17 00:00:00 2001
From: Nouha-ElAbrouki <97687698+Noha-ElAbrouki@users.noreply.github.com>
Date: Fri, 29 Jul 2022 10:17:32 +0200
Subject: [PATCH 03/18] enh(Header/userMenu):reduce spacing user menu (#11393)
* update user menu
---
www/front_src/src/Header/Clock/index.tsx | 6 +-
.../Header/SwitchThemeMode/images/moon.svg | 3 -
.../src/Header/SwitchThemeMode/images/sun.svg | 3 -
.../src/Header/SwitchThemeMode/index.tsx | 135 ++++++++----------
.../SwitchThemeMode/useSwitchThemeMode.tsx | 30 ++++
www/front_src/src/Header/helpers/index.ts | 5 +
www/front_src/src/Header/index.tsx | 17 ++-
.../src/Header/userMenu/index.test.tsx | 6 +-
www/front_src/src/Header/userMenu/index.tsx | 127 ++++++++++++----
9 files changed, 210 insertions(+), 122 deletions(-)
delete mode 100644 www/front_src/src/Header/SwitchThemeMode/images/moon.svg
delete mode 100644 www/front_src/src/Header/SwitchThemeMode/images/sun.svg
create mode 100644 www/front_src/src/Header/SwitchThemeMode/useSwitchThemeMode.tsx
create mode 100644 www/front_src/src/Header/helpers/index.ts
diff --git a/www/front_src/src/Header/Clock/index.tsx b/www/front_src/src/Header/Clock/index.tsx
index 2a96282918e..2eaf875fa91 100755
--- a/www/front_src/src/Header/Clock/index.tsx
+++ b/www/front_src/src/Header/Clock/index.tsx
@@ -3,7 +3,7 @@ import { useRef, useState, useEffect } from 'react';
import { Typography } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
-import { useLocaleDateTimeFormat } from '@centreon/ui';
+import { centreonUi } from '../helpers/index';
const useStyles = makeStyles((theme) => ({
dateTime: {
@@ -20,7 +20,7 @@ const Clock = (): JSX.Element => {
time: '',
});
- const { format, toTime } = useLocaleDateTimeFormat();
+ const { format, toTime } = centreonUi.useLocaleDateTimeFormat();
const updateDateTime = (): void => {
const now = new Date();
@@ -48,7 +48,7 @@ const Clock = (): JSX.Element => {
const { date, time } = dateTime;
return (
-
+
{date}
{time}
diff --git a/www/front_src/src/Header/SwitchThemeMode/images/moon.svg b/www/front_src/src/Header/SwitchThemeMode/images/moon.svg
deleted file mode 100644
index 4c16f826815..00000000000
--- a/www/front_src/src/Header/SwitchThemeMode/images/moon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/www/front_src/src/Header/SwitchThemeMode/images/sun.svg b/www/front_src/src/Header/SwitchThemeMode/images/sun.svg
deleted file mode 100644
index c819f5b586d..00000000000
--- a/www/front_src/src/Header/SwitchThemeMode/images/sun.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/www/front_src/src/Header/SwitchThemeMode/index.tsx b/www/front_src/src/Header/SwitchThemeMode/index.tsx
index 8a3eca92bda..ef83ec81ebf 100644
--- a/www/front_src/src/Header/SwitchThemeMode/index.tsx
+++ b/www/front_src/src/Header/SwitchThemeMode/index.tsx
@@ -1,105 +1,70 @@
-import { equals } from 'ramda';
-import { useAtom } from 'jotai';
+import { useState } from 'react';
+
+import clsx from 'clsx';
import { useLocation } from 'react-router-dom';
-import { styled } from '@mui/material/styles';
-import Switch from '@mui/material/Switch';
+import { ListItemText, Switch } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
-import { userAtom, ThemeMode } from '@centreon/ui-context';
import { patchData, useRequest } from '@centreon/ui';
-import svgSun from './images/sun.svg';
-import svgMoon from './images/moon.svg';
-
-interface StyleProps {
- darkModeSvg?: string;
- lightModeSvg?: string;
-}
+import useSwitchThemeMode from './useSwitchThemeMode';
-const ThemeModeSwitch = styled(Switch, {
- shouldForwardProp: (prop) =>
- !equals(prop, 'color') &&
- !equals(prop, 'lightModeSvg') &&
- !equals(prop, 'darkModeSvg'),
-})
(({ theme, darkModeSvg, lightModeSvg }) => ({
- '& .MuiSwitch-switchBase': {
+const useStyles = makeStyles((theme) => ({
+ container: {
+ '& .MuiSwitch-thumb': {
+ backgroundColor: 'white',
+ },
+ '& .MuiSwitch-track': {
+ backgroundColor: '#aab4be',
+ opacity: 1,
+ },
+ alignItems: 'center',
+ display: 'flex',
+ },
+ containerMode: {
+ display: 'flex',
+ justifyContent: 'space-around',
+ },
+ containerSwitch: {
+ '& .MuiSwitch-switchBase': {
+ padding: theme.spacing(0.5, 0.5, 0.5, 0.75),
+ },
'&.Mui-checked': {
- '& + .MuiSwitch-track': {
- backgroundColor: '#aab4be',
- opacity: 1,
- },
- '& .MuiSwitch-thumb:before': {
- backgroundImage: `url(${darkModeSvg})`,
+ '&:hover': {
+ backgroundColor: 'unset',
},
- color: 'transparent',
- transform: 'translate(15px,-50%)',
},
'&:hover': {
- backgroundColor: 'transparent',
+ backgroundColor: 'unset',
},
- color: 'black',
- margin: 0,
- position: 'absolute',
- top: '50%',
- transform: 'translate(-0.5px,-50%)',
},
- '& .MuiSwitch-thumb': {
- '&:before': {
- backgroundImage: `url(${lightModeSvg})`,
- backgroundPosition: 'center',
- backgroundRepeat: 'no-repeat',
- content: "''",
- height: '100%',
- left: theme.spacing(0),
- position: 'absolute',
- top: theme.spacing(0),
- width: '100%',
- },
- backgroundColor: 'white',
- height: theme.spacing(3),
- width: theme.spacing(3),
+ disabledMode: {
+ color: theme.palette.common.white,
+ opacity: 0.5,
},
- '& .MuiSwitch-track': {
- backgroundColor: '#aab4be',
- borderRadius: theme.spacing(10 / 8),
- opacity: 1,
- },
- height: theme.spacing(32 / 8),
- padding: theme.spacing(11 / 8, 4 / 8, 11 / 8, 9 / 8),
- width: theme.spacing(50 / 8),
-}));
-
-const useStyles = makeStyles(() => ({
- container: {
- alignItems: 'center',
- display: 'flex',
+ mode: {
+ paddingLeft: theme.spacing(1),
},
}));
const SwitchThemeMode = (): JSX.Element => {
- const props = {
- darkModeSvg: svgMoon,
- lightModeSvg: svgSun,
- };
const classes = useStyles();
const { pathname } = useLocation();
+ const [isPending, isDarkMode, themeMode, updateUser] = useSwitchThemeMode();
+
+ const [isDark, setIsDark] = useState(isDarkMode);
const { sendRequest } = useRequest({
request: patchData,
});
- const [user, setUser] = useAtom(userAtom);
- const isDarkMode = equals(user.themeMode, ThemeMode.dark);
const switchEndPoint = './api/latest/configuration/users/current/parameters';
const switchThemeMode = (): void => {
- const themeMode = isDarkMode ? ThemeMode.light : ThemeMode.dark;
const isCurrentPageLegacy = pathname.includes('php');
- setUser({
- ...user,
- themeMode,
- });
+ setIsDark(!isDark);
+ updateUser();
sendRequest({
data: { theme: themeMode },
endpoint: switchEndPoint,
@@ -112,11 +77,29 @@ const SwitchThemeMode = (): JSX.Element => {
return (
-
+
+
+ Light
+
+
+
+ Dark
+
+
);
};
diff --git a/www/front_src/src/Header/SwitchThemeMode/useSwitchThemeMode.tsx b/www/front_src/src/Header/SwitchThemeMode/useSwitchThemeMode.tsx
new file mode 100644
index 00000000000..f25adff71f7
--- /dev/null
+++ b/www/front_src/src/Header/SwitchThemeMode/useSwitchThemeMode.tsx
@@ -0,0 +1,30 @@
+import { useTransition } from 'react';
+
+import { useAtom } from 'jotai';
+import { equals } from 'ramda';
+
+import { userAtom, ThemeMode } from '@centreon/ui-context';
+
+const useSwitchThemeMode = (): [
+ isDarkMode: boolean,
+ isPending: boolean,
+ themeMode: ThemeMode,
+ updateUser: () => void,
+] => {
+ const [user, setUser] = useAtom(userAtom);
+ const isDarkMode = equals(user.themeMode, ThemeMode.dark);
+ const [isPending, startTransition] = useTransition();
+
+ const themeMode = isDarkMode ? ThemeMode.light : ThemeMode.dark;
+ const updateUser = (): void =>
+ startTransition(() => {
+ setUser({
+ ...user,
+ themeMode,
+ });
+ });
+
+ return [isPending, isDarkMode, themeMode, updateUser];
+};
+
+export default useSwitchThemeMode;
diff --git a/www/front_src/src/Header/helpers/index.ts b/www/front_src/src/Header/helpers/index.ts
new file mode 100644
index 00000000000..62d01d70451
--- /dev/null
+++ b/www/front_src/src/Header/helpers/index.ts
@@ -0,0 +1,5 @@
+import { useLocaleDateTimeFormat } from '@centreon/ui';
+
+export const centreonUi = {
+ useLocaleDateTimeFormat,
+};
diff --git a/www/front_src/src/Header/index.tsx b/www/front_src/src/Header/index.tsx
index 3d1262da36d..dbf1e2c3cfd 100755
--- a/www/front_src/src/Header/index.tsx
+++ b/www/front_src/src/Header/index.tsx
@@ -1,3 +1,5 @@
+import { useRef } from 'react';
+
import { makeStyles } from '@mui/styles';
import Hook from '../components/Hook';
@@ -6,7 +8,6 @@ import PollerMenu from './PollerMenu';
import HostStatusCounter from './RessourceStatusCounter/Host';
import ServiceStatusCounter from './RessourceStatusCounter/Service';
import UserMenu from './userMenu';
-import SwitchMode from './SwitchThemeMode';
const HookComponent = Hook as unknown as (props) => JSX.Element;
@@ -30,12 +31,12 @@ const useStyles = makeStyles((theme) => ({
justifyContent: 'center',
},
pollerContainer: {
- flex: 0.5,
+ flex: 0.4,
},
rightContainer: {
alignItems: 'center',
display: 'flex',
- flex: 1.1,
+ flex: 0.9,
},
serviceStatusContainer: {
display: 'flex',
@@ -49,16 +50,17 @@ const useStyles = makeStyles((theme) => ({
userMenuContainer: {
alignItems: 'center',
display: 'flex',
- flex: 0.4,
+ flex: 0.3,
justifyContent: 'flex-end',
},
}));
const Header = (): JSX.Element => {
const classes = useStyles();
+ const headerRef = useRef(null);
return (
-
+
@@ -74,10 +76,7 @@ const Header = (): JSX.Element => {
diff --git a/www/front_src/src/Header/userMenu/index.test.tsx b/www/front_src/src/Header/userMenu/index.test.tsx
index 471ef21a908..2878468f19a 100644
--- a/www/front_src/src/Header/userMenu/index.test.tsx
+++ b/www/front_src/src/Header/userMenu/index.test.tsx
@@ -109,11 +109,9 @@ describe('User Menu', () => {
userEvent.click(screen.getByLabelText(labelProfile));
await waitFor(() => {
- expect(screen.getByText('Admin admin')).toBeInTheDocument();
+ expect(screen.getByText('admin')).toBeInTheDocument();
});
- expect(screen.getByText('as admin')).toBeInTheDocument();
-
await waitFor(() => {
expect(screen.getByText('1:20 PM')).toBeInTheDocument();
});
@@ -139,7 +137,7 @@ describe('User Menu', () => {
});
await waitFor(() => {
- expect(screen.getByText('Admin admin')).toBeInTheDocument();
+ expect(screen.getByText('admin')).toBeInTheDocument();
});
userEvent.click(screen.getByText(labelCopyAutologinLink));
diff --git a/www/front_src/src/Header/userMenu/index.tsx b/www/front_src/src/Header/userMenu/index.tsx
index 45adb4fc4e7..9b66220f892 100755
--- a/www/front_src/src/Header/userMenu/index.tsx
+++ b/www/front_src/src/Header/userMenu/index.tsx
@@ -6,6 +6,8 @@ import { useNavigate } from 'react-router-dom';
import { useUpdateAtom } from 'jotai/utils';
import { gt, isNil, not, __ } from 'ramda';
+import { grey } from '@mui/material/colors';
+import Divider from '@mui/material/Divider';
import {
Typography,
Paper,
@@ -35,6 +37,7 @@ import {
useLocaleDateTimeFormat,
} from '@centreon/ui';
+import SwitchMode from '../SwitchThemeMode/index';
import Clock from '../Clock';
import useNavigation from '../../Navigation/useNavigation';
import { areUserParametersLoadedAtom } from '../../Main/useUser';
@@ -79,6 +82,27 @@ const ListItemIcon = styled(MUIListItemIcon)(({ theme }) => ({
}));
const useStyles = makeStyles((theme) => ({
+ button: {
+ '&:hover': {
+ '&:after': {
+ backgroundColor: theme.palette.common.white,
+ content: '""',
+ height: '100%',
+ left: 0,
+ opacity: 0.08,
+ position: 'absolute',
+ right: 0,
+ top: 0,
+ },
+ },
+ },
+ containerList: {
+ padding: theme.spacing(0.5, 0, 0.5, 0),
+ },
+ divider: {
+ borderColor: grey[600],
+ margin: theme.spacing(0, 1.25, 0, 1.25),
+ },
fullname: {
overflow: 'hidden',
textOverflow: 'ellipsis',
@@ -91,14 +115,23 @@ const useStyles = makeStyles((theme) => ({
top: theme.spacing(-13),
width: theme.spacing(0),
},
+ icon: {
+ minWidth: theme.spacing(3.75),
+ },
loaderUserMenu: {
- marginRight: 22,
+ marginRight: theme.spacing(22 / 8),
},
menu: {
backgroundColor: theme.palette.common.black,
+ borderRadius: 0,
color: theme.palette.common.white,
- maxWidth: 230,
- width: '100%',
+ minWidth: 190,
+ },
+ menuItem: {
+ padding: theme.spacing(0, 2, 0.25, 2),
+ },
+ nameContainer: {
+ padding: theme.spacing(0, 2, 0.25, 2.25),
},
passwordExpiration: {
color: theme.palette.warning.main,
@@ -107,6 +140,9 @@ const useStyles = makeStyles((theme) => ({
overflow: 'hidden',
zIndex: theme.zIndex.tooltip,
},
+ switchItem: {
+ padding: theme.spacing(0, 2, 0.25, 11 / 8),
+ },
text: {
overflow: 'hidden',
textOverflow: 'ellipsis',
@@ -119,6 +155,7 @@ const useStyles = makeStyles((theme) => ({
},
wrapRightUser: {
alignItems: 'center',
+ background: theme.palette.common.black,
display: 'flex',
flexWrap: 'wrap',
marginLeft: theme.spacing(0.5),
@@ -131,8 +168,11 @@ const useStyles = makeStyles((theme) => ({
justifyContent: 'flex-end',
},
}));
+interface Props {
+ headerRef?: RefObject;
+}
-const UserMenu = (): JSX.Element => {
+const UserMenu = ({ headerRef }: Props): JSX.Element => {
const classes = useStyles();
const { t } = useTranslation();
const { allowedPages } = useNavigation();
@@ -140,10 +180,12 @@ const UserMenu = (): JSX.Element => {
const [copied, setCopied] = useState(false);
const [data, setData] = useState(null);
const [anchorEl, setAnchorEl] = useState(null);
+ const [anchorHeight, setAnchorHeight] = useState(12);
const profile = useRef();
const userMenu = useRef();
const autologinNode = useRef();
const refreshTimeout = useRef();
+ const userIconRef = useRef(null);
const { sendRequest: logoutRequest } = useRequest({
request: postData,
});
@@ -198,6 +240,21 @@ const UserMenu = (): JSX.Element => {
}, 60000);
};
+ const getPositionOfPopper = (): void => {
+ if (isNil(headerRef?.current) || isNil(userIconRef?.current)) {
+ return;
+ }
+ const headerHeight = headerRef?.current?.getBoundingClientRect()?.height;
+
+ const userMenuBottom =
+ userIconRef?.current?.getBoundingClientRect()?.bottom;
+
+ if (isNil(headerHeight)) {
+ return;
+ }
+ setAnchorHeight(headerHeight - userMenuBottom);
+ };
+
const toggle = (event: MouseEvent): void => {
if (anchorEl) {
setAnchorEl(null);
@@ -205,6 +262,7 @@ const UserMenu = (): JSX.Element => {
return;
}
setAnchorEl(event.currentTarget);
+ getPositionOfPopper();
};
const closeUserMenu = (): void => {
@@ -246,10 +304,14 @@ const UserMenu = (): JSX.Element => {
useEffect(() => {
window.addEventListener('mousedown', handleClick, false);
+ window.addEventListener('resize', getPositionOfPopper);
+
loadUserData();
return (): void => {
window.removeEventListener('mousedown', handleClick, false);
+ window.removeEventListener('resize', getPositionOfPopper);
+
if (refreshTimeout.current) {
clearTimeout(refreshTimeout.current);
}
@@ -279,9 +341,9 @@ const UserMenu = (): JSX.Element => {
};
return (
-
+
}
>
@@ -302,8 +364,10 @@ const UserMenu = (): JSX.Element => {
>
@@ -312,8 +376,16 @@ const UserMenu = (): JSX.Element => {
transition
anchorEl={anchorEl}
className={classes.popper}
+ data-cy="popper"
+ modifiers={[
+ {
+ name: 'offset',
+ options: {
+ offset: [22, anchorHeight],
+ },
+ },
+ ]}
open={not(isNil(anchorEl))}
- placement="bottom-end"
>
{({ TransitionProps }): JSX.Element => (
@@ -324,21 +396,18 @@ const UserMenu = (): JSX.Element => {
display: isNil(anchorEl) ? 'none' : 'block',
}}
>
-
-
+
+
- {data.fullname}
+ {data.username}
-
- {`${t('as')} ${data.username}`}
-
+
+
{not(passwordIsNotYetAboutToExpire) && (
-
+
{t(labelPasswordWillExpireIn)}:
@@ -350,11 +419,12 @@ const UserMenu = (): JSX.Element => {
)}
{allowEditProfile && (
-
+
-
+
{t(labelEditProfile)}
@@ -362,9 +432,9 @@ const UserMenu = (): JSX.Element => {
)}
{data.autologinkey && (
-
+
-
+
{copied ? (
) : (
@@ -384,9 +454,18 @@ const UserMenu = (): JSX.Element => {
/>
)}
-
-
-
+
+
+
+
+
+
+
+
+
{t(labelLogout)}
From 7737c376d240bc33e21300b8b1a8d4d55666f3e8 Mon Sep 17 00:00:00 2001
From: jeremyjaouen <61694165+jeremyjaouen@users.noreply.github.com>
Date: Fri, 29 Jul 2022 10:24:52 +0200
Subject: [PATCH 04/18] fix(hostgroup): fix display of hostgroups in select2
(#11431) (#11443)
---
www/class/centreonHostgroups.class.php | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/www/class/centreonHostgroups.class.php b/www/class/centreonHostgroups.class.php
index c8c5b1225a2..34dd68d6947 100644
--- a/www/class/centreonHostgroups.class.php
+++ b/www/class/centreonHostgroups.class.php
@@ -334,6 +334,12 @@ public function getObjectForSelect2($values = array(), $options = array())
return $items;
}
+ $hostgroups = [];
+ // $values structure: ['1,2,3,4'], keeping the foreach in case it could have more than one index
+ foreach ($values as $value) {
+ $hostgroups = array_merge($hostgroups, explode(',', $value));
+ }
+
// get list of authorized hostgroups
if (!$centreon->user->access->admin) {
$hgAcl = $centreon->user->access->getHostGroupAclConf(
@@ -347,7 +353,7 @@ public function getObjectForSelect2($values = array(), $options = array())
'conditions' => array(
'hostgroup.hg_id' => array(
'IN',
- $values
+ $hostgroups
)
)
),
@@ -359,15 +365,13 @@ public function getObjectForSelect2($values = array(), $options = array())
$listValues = '';
$queryValues = array();
- foreach ($values as $k => $v) {
- //As it happens that $v could be like "X,Y" when two hostgroups are selected, we added a second foreach
- $multiValues = explode(',', $v);
- foreach ($multiValues as $item) {
- $ids = explode('-', $item);
- $listValues .= ':hgId_' . $ids[0] . ', ';
- $queryValues['hgId_' . $ids[0]] = (int)$ids[0];
- }
+ foreach ($hostgroups as $item) {
+ // the below explode may not be useful
+ $ids = explode('-', $item);
+ $listValues .= ':hgId_' . $ids[0] . ', ';
+ $queryValues['hgId_' . $ids[0]] = (int)$ids[0];
}
+
$listValues = rtrim($listValues, ', ');
$query = 'SELECT hg_id, hg_name FROM hostgroup WHERE hg_id IN (' . $listValues . ') ORDER BY hg_name ';
$stmt = $this->DB->prepare($query);
From 038b6fca98f74fc7502615df8a2636b756ee41f7 Mon Sep 17 00:00:00 2001
From: Kevin Duret
Date: Fri, 29 Jul 2022 11:03:34 +0200
Subject: [PATCH 05/18] fix(ci): fix debian packaging with freshly instanciated
jenkins slave (#11398) (#11399)
Refs: MON-14377
---
ci/scripts/centreon-deb-package.sh | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/ci/scripts/centreon-deb-package.sh b/ci/scripts/centreon-deb-package.sh
index d7a1cdcca36..01d7bafa875 100755
--- a/ci/scripts/centreon-deb-package.sh
+++ b/ci/scripts/centreon-deb-package.sh
@@ -38,10 +38,9 @@ for i in lang/* ; do
done
rm -rf lang
-# Generate API documentation.
-apt install -y npm && sleep 30
-npm install -g redoc-cli
-/usr/local/bin/redoc-cli bundle --options.hideDownloadButton=true doc/API/centreon-api-v${MAJOR_VERSION}.yaml -o ../centreon-api-v${MAJOR_VERSION}.html
+# Install npm dependency
+apt-get update
+apt install -y npm
# Make tar with original content
cd ..
From 35967db531c1ab9421aed1da17c9d2041127e40e Mon Sep 17 00:00:00 2001
From: Elmahdi ABBASSI <108519266+emabassi-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 10:29:10 +0100
Subject: [PATCH 06/18] Sanitized and bound queries (#11413) (#11445)
lines : 130 -142
---
.../hostgroup_dependency/DB-Func.php | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/www/include/configuration/configObject/hostgroup_dependency/DB-Func.php b/www/include/configuration/configObject/hostgroup_dependency/DB-Func.php
index ded4d3c58ff..1ddeac48d46 100644
--- a/www/include/configuration/configObject/hostgroup_dependency/DB-Func.php
+++ b/www/include/configuration/configObject/hostgroup_dependency/DB-Func.php
@@ -124,10 +124,12 @@ function multipleHostGroupDependencyInDB($dependencies = array(), $nbrDup = arra
"WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hgParents"] = "";
+ $query = "INSERT INTO dependency_hostgroupParent_relation VALUES (:max_id, :hg_id)";
+ $statement = $pearDB->prepare($query);
while ($hg = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_hostgroupParent_relation VALUES ('" .
- $maxId["MAX(dep_id)"] . "', '" . $hg["hostgroup_hg_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':max_id', (int) $maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':hg_id', (int) $hg["hostgroup_hg_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hgParents"] .= $hg["hostgroup_hg_id"] . ",";
}
$fields["dep_hgParents"] = trim($fields["dep_hgParents"], ",");
@@ -136,10 +138,12 @@ function multipleHostGroupDependencyInDB($dependencies = array(), $nbrDup = arra
"WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hgChilds"] = "";
+ $query = "INSERT INTO dependency_hostgroupChild_relation VALUES (:max_id, :hg_id)";
+ $statement = $pearDB->prepare($query);
while ($hg = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_hostgroupChild_relation VALUES ('" .
- $maxId["MAX(dep_id)"] . "', '" . $hg["hostgroup_hg_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':max_id', (int) $maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':hg_id', (int) $hg["hostgroup_hg_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hgChilds"] .= $hg["hostgroup_hg_id"] . ",";
}
$fields["dep_hgChilds"] = trim($fields["dep_hgChilds"], ",");
From ddd8d277bc257ef4bc8b53bc523f4a3fa1c1f358 Mon Sep 17 00:00:00 2001
From: hyahiaoui-ext <97593234+hyahiaoui-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 11:07:42 +0100
Subject: [PATCH 07/18] Snyk: Sanitize and bind media sync queries 22.04.x
(#11418)
* sanitizing and binding sync dir file queries
* Applying some fixes
---
www/include/options/media/images/syncDir.php | 30 +++++++++++++-------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/www/include/options/media/images/syncDir.php b/www/include/options/media/images/syncDir.php
index b9eee03bb0c..f6e2075a36a 100644
--- a/www/include/options/media/images/syncDir.php
+++ b/www/include/options/media/images/syncDir.php
@@ -173,12 +173,17 @@ function checkPicture($picture, $dirpath, $dir_id, $pearDB)
$gdCounter++;
}
- $DBRESULT = $pearDB->query("SELECT img_id " .
+ $statement = $pearDB->prepare(
+ "SELECT img_id " .
"FROM view_img, view_img_dir_relation vidh " .
- "WHERE img_path = '" . $picture . "' " .
- " AND vidh.dir_dir_parent_id = '" . $dir_id . "'" .
- " AND vidh.img_img_id = img_id");
- if (!$DBRESULT->rowCount()) {
+ "WHERE img_path = :img_path " .
+ "AND vidh.dir_dir_parent_id = :dir_dir_parent_id " .
+ "AND vidh.img_img_id = img_id"
+ );
+ $statement->bindValue(':img_path', $picture, \PDO::PARAM_STR);
+ $statement->bindValue(':dir_dir_parent_id', (int) $dir_id, \PDO::PARAM_INT);
+ $statement->execute();
+ if (!$statement->rowCount()) {
$DBRESULT = $pearDB->query(
"INSERT INTO view_img (`img_name`, `img_path`) VALUES ('"
. $img_info["filename"] . "', '" . $picture . "')"
@@ -189,13 +194,16 @@ function checkPicture($picture, $dirpath, $dir_id, $pearDB)
);
$data = $DBRESULT->fetchRow();
$regCounter++;
- $DBRESULT = $pearDB->query(
- "INSERT INTO view_img_dir_relation (`dir_dir_parent_id`, `img_img_id`) VALUES ('"
- . $dir_id . "', '" . $data['img_id'] . "')"
+ $statement = $pearDB->prepare(
+ "INSERT INTO view_img_dir_relation (`dir_dir_parent_id`, `img_img_id`)
+ VALUES (:dir_dir_parent_id, :img_img_id)"
);
+ $statement->bindValue(':dir_dir_parent_id', (int) $dir_id, \PDO::PARAM_INT);
+ $statement->bindValue(':img_img_id', (int) $data['img_id'], \PDO::PARAM_INT);
+ $statement->execute();
return $data['img_id'];
} else {
- $data = $DBRESULT->fetchRow();
+ $data = $statement->fetchRow(\PDO::FETCH_ASSOC);
return 0;
}
}
@@ -211,9 +219,11 @@ function DeleteOldPictures($pearDB)
. "view_img_dir vid, view_img_dir_relation vidr "
. "WHERE vidr.img_img_id = vi.img_id AND vid.dir_id = vidr.dir_dir_parent_id"
);
+ $statement = $pearDB->prepare("DELETE FROM view_img WHERE img_id = :img_id");
while ($row2 = $DBRESULT->fetchRow()) {
if (!file_exists("./img/media/" . $row2["dir_alias"] . "/" . $row2["img_path"])) {
- $pearDB->query("DELETE FROM view_img WHERE img_id = '" . $row2["img_id"] . "'");
+ $statement->bindValue(':img_id', (int) $row2["img_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fileRemoved++;
}
}
From 9db3d7ed529e5fb995fe073c075df82d292e157b Mon Sep 17 00:00:00 2001
From: hyahiaoui-ext <97593234+hyahiaoui-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 11:08:45 +0100
Subject: [PATCH 08/18] Snyk: Sanitize and bind ACL service dependency queries
dev-22.04.x (#11395)
---
.../service_dependency/DB-Func.php | 37 ++++++++++++++-----
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/www/include/configuration/configObject/service_dependency/DB-Func.php b/www/include/configuration/configObject/service_dependency/DB-Func.php
index 501d43c5f2a..573e2b5e62b 100644
--- a/www/include/configuration/configObject/service_dependency/DB-Func.php
+++ b/www/include/configuration/configObject/service_dependency/DB-Func.php
@@ -127,10 +127,12 @@ function multipleServiceDependencyInDB($dependencies = array(), $nbrDup = array(
$query = "SELECT * FROM dependency_hostChild_relation WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hostPar"] = "";
+ $query = "INSERT INTO dependency_hostChild_relation VALUES (:dep_id, :host_host_id)";
+ $statement = $pearDB->prepare($query);
while ($host = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_hostChild_relation VALUES ('" . $maxId["MAX(dep_id)"] .
- "', '" . $host["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':dep_id', (int) $maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(':host_host_id', (int) $host["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hostPar"] .= $host["host_host_id"] . ",";
}
$fields["dep_hostPar"] = trim($fields["dep_hostPar"], ",");
@@ -138,21 +140,36 @@ function multipleServiceDependencyInDB($dependencies = array(), $nbrDup = array(
$query = "SELECT * FROM dependency_serviceParent_relation WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hSvPar"] = "";
+ $query = "INSERT INTO dependency_serviceParent_relation
+ VALUES (:dep_id, :service_service_id, :host_host_id)";
+ $statement = $pearDB->prepare($query);
while ($service = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_serviceParent_relation VALUES ('" .
- $maxId["MAX(dep_id)"] . "', '" . $service["service_service_id"] . "', '" .
- $service["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':dep_id', (int) $maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(
+ ':service_service_id',
+ (int) $service["service_service_id"],
+ \PDO::PARAM_INT
+ );
+ $statement->bindValue(':host_host_id', (int) $service["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hSvPar"] .= $service["service_service_id"] . ",";
}
$fields["dep_hSvPar"] = trim($fields["dep_hSvPar"], ",");
$query = "SELECT * FROM dependency_serviceChild_relation WHERE dependency_dep_id = '" . $key . "'";
$dbResult = $pearDB->query($query);
$fields["dep_hSvChi"] = "";
+ $query = "INSERT INTO dependency_serviceChild_relation
+ VALUES (:dep_id, :service_service_id, :host_host_id)";
+ $statement = $pearDB->prepare($query);
while ($service = $dbResult->fetch()) {
- $query = "INSERT INTO dependency_serviceChild_relation VALUES ('" . $maxId["MAX(dep_id)"] .
- "', '" . $service["service_service_id"] . "', '" . $service["host_host_id"] . "')";
- $pearDB->query($query);
+ $statement->bindValue(':dep_id', (int) $maxId["MAX(dep_id)"], \PDO::PARAM_INT);
+ $statement->bindValue(
+ ':service_service_id',
+ (int) $service["service_service_id"],
+ \PDO::PARAM_INT
+ );
+ $statement->bindValue(':host_host_id', (int) $service["host_host_id"], \PDO::PARAM_INT);
+ $statement->execute();
$fields["dep_hSvChi"] .= $service["service_service_id"] . ",";
}
$fields["dep_hSvChi"] = trim($fields["dep_hSvChi"], ",");
From 9b61eb73775ed2739139d891040b0a4f833effb7 Mon Sep 17 00:00:00 2001
From: hyahiaoui-ext <97593234+hyahiaoui-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 11:09:13 +0100
Subject: [PATCH 09/18] Snyk: Sanitize and bind Auth class queries 22.04.x
(#11448)
---
www/class/centreonAuth.class.php | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/www/class/centreonAuth.class.php b/www/class/centreonAuth.class.php
index a2ea42427bb..e05ca3ce06f 100644
--- a/www/class/centreonAuth.class.php
+++ b/www/class/centreonAuth.class.php
@@ -339,12 +339,13 @@ protected function checkUser($username, $password, $token)
if ($dbResult->rowCount()) {
$this->userInfos = $dbResult->fetch();
if ($this->userInfos["default_page"]) {
- $dbResult2 = $this->pearDB->query(
- "SELECT topology_url_opt FROM topology WHERE topology_page = "
- . $this->userInfos["default_page"]
+ $statement = $this->pearDB->prepare(
+ "SELECT topology_url_opt FROM topology WHERE topology_page = :topology_page"
);
- if ($dbResult2->numRows()) {
- $data = $dbResult2->fetch();
+ $statement->bindValue(':topology_page', (int) $this->userInfos["default_page"], \PDO::PARAM_INT);
+ $statement->execute();
+ if ($statement->rowCount()) {
+ $data = $statement->fetch(\PDO::FETCH_ASSOC);
$this->userInfos["default_page"] .= $data["topology_url_opt"];
}
}
@@ -382,20 +383,23 @@ protected function checkUser($username, $password, $token)
/*
* Reset userInfos with imported information
*/
- $dbResult = $this->pearDB->query(
+ $statement = $this->pearDB->prepare(
"SELECT * FROM `contact` " .
- "WHERE `contact_alias` = '" . $this->pearDB->escape($username, true) . "'" .
+ "WHERE `contact_alias` = :contact_alias" .
"AND `contact_activate` = '1' AND `contact_register` = '1' LIMIT 1"
);
- if ($dbResult->rowCount()) {
- $this->userInfos = $dbResult->fetch();
+ $statement->bindValue(':contact_alias', $this->pearDB->escape($username, true), \PDO::PARAM_STR);
+ $statement->execute();
+ if ($statement->rowCount()) {
+ $this->userInfos = $statement->fetch(\PDO::FETCH_ASSOC);
if ($this->userInfos["default_page"]) {
- $dbResult2 = $this->pearDB->query(
- "SELECT topology_url_opt FROM topology WHERE topology_page = "
- . $this->userInfos["default_page"]
+ $statement = $this->pearDB->prepare(
+ "SELECT topology_url_opt FROM topology WHERE topology_page = :topology_page"
);
- if ($dbResult2->numRows()) {
- $data = $dbResult2->fetch();
+ $statement->bindValue(':topology_page', (int) $this->userInfos["default_page"], \PDO::PARAM_INT);
+ $statement->execute();
+ if ($statement->rowCount()) {
+ $data = $statement->fetch(\PDO::FETCH_ASSOC);
$this->userInfos["default_page"] .= $data["topology_url_opt"];
}
}
From 8d64b02775048277fe469e79c6c8d639df6c7c59 Mon Sep 17 00:00:00 2001
From: Tom Darneix
Date: Fri, 29 Jul 2022 12:20:13 +0200
Subject: [PATCH 10/18] [Backport/need review] fix(UI): Fix layout for Safari
and form validation (#11440)
* fix(UI): Fix layout for Safari and form validation (#11373)
* Fix form validation
* Fix padlock layout for safari
* Update centreon-frontend
* Remove debug variable
* Fix test
* Fix page respsoniveness
* Rename variable
* update deps
* Fix package-lock
* Fix package-lock
* Add debug statement for debian
* Install nodejs rather npm
* Attempt fix
* Attempt to fix nodejs installation
* add sudo
* Fix redoc-cli usage
* Try to fix permission on npm
* Fix
* Fix permission
* Fix permission (please work)
* Fix source
* Stop using npx because.....
* Allow legacy-peer-deps
* Remove nodejs installation
* Fix image to pull for debian 11
---
Jenkinsfile | 2 +-
ci/debian/rules | 2 +-
ci/scripts/centreon-deb-package.sh | 6 +-
.../src/Authentication/Openid/Form/inputs.ts | 18 +----
.../src/Authentication/Openid/index.test.tsx | 41 +---------
.../Openid/useValidationSchema.ts | 11 +--
www/front_src/src/Authentication/index.tsx | 74 ++++++++++++-------
7 files changed, 56 insertions(+), 98 deletions(-)
diff --git a/Jenkinsfile b/Jenkinsfile
index 0703e55f23c..e5895522af5 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -308,7 +308,7 @@ try {
checkout scm
}
sh 'rm -rf *.deb'
- sh 'docker run -i --entrypoint /src/centreon/ci/scripts/centreon-deb-package.sh -w "/src" -v "$PWD:/src" -e DISTRIB="bullseye" -e VERSION=$VERSION -e RELEASE=$RELEASE registry.centreon.com/centreon-debian11-dependencies:22.04'
+ sh 'docker run -i --entrypoint /src/centreon/ci/scripts/centreon-deb-package.sh -w "/src" -v "$PWD:/src" -e DISTRIB="bullseye" -e VERSION=$VERSION -e RELEASE=$RELEASE registry.centreon.com/mon-build-dependencies-22.04:debian11'
stash name: 'Debian11', includes: '*.deb'
archiveArtifacts artifacts: "*"
sh 'rm -rf *.deb'
diff --git a/ci/debian/rules b/ci/debian/rules
index 0e8ee8a1a3c..287f52a3658 100644
--- a/ci/debian/rules
+++ b/ci/debian/rules
@@ -13,7 +13,7 @@ override_dh_clean:
override_dh_auto_build:
composer install --no-dev --optimize-autoloader -n
- npm ci
+ npm ci --legacy-peer-deps
npm run build
find . -type f | \
grep -v debian/extra/centreon-web/centreon-macroreplacement.txt | \
diff --git a/ci/scripts/centreon-deb-package.sh b/ci/scripts/centreon-deb-package.sh
index 01d7bafa875..be700cfccb0 100755
--- a/ci/scripts/centreon-deb-package.sh
+++ b/ci/scripts/centreon-deb-package.sh
@@ -38,9 +38,9 @@ for i in lang/* ; do
done
rm -rf lang
-# Install npm dependency
-apt-get update
-apt install -y npm
+# Generate API documentation.
+npm i -g redoc-cli
+redoc-cli build --options.hideDownloadButton=true doc/API/centreon-api-v${MAJOR_VERSION}.yaml -o ../centreon-api-v${MAJOR_VERSION}.html
# Make tar with original content
cd ..
diff --git a/www/front_src/src/Authentication/Openid/Form/inputs.ts b/www/front_src/src/Authentication/Openid/Form/inputs.ts
index 01c07962a97..55b3b1fa125 100644
--- a/www/front_src/src/Authentication/Openid/Form/inputs.ts
+++ b/www/front_src/src/Authentication/Openid/Form/inputs.ts
@@ -1,4 +1,4 @@
-import { equals, isEmpty, isNil, not, path, prop } from 'ramda';
+import { equals, isEmpty, not, prop } from 'ramda';
import { FormikValues } from 'formik';
import {
@@ -32,7 +32,7 @@ import {
labelDeleteRelation,
labelAuthorizationKey,
} from '../translatedLabels';
-import { AuthenticationType, AuthorizationRule } from '../models';
+import { AuthenticationType } from '../models';
import { InputProps, InputType } from '../../FormInputs/models';
import {
labelActivation,
@@ -249,20 +249,6 @@ export const inputs: Array = [
claimValue: '',
},
deleteLabel: labelDeleteRelation,
- getRequired: ({ values, index }): boolean => {
- const tableValues = prop('authorizationRules', values);
-
- const rowValues = path(
- ['authorizationRules', index],
- values,
- );
-
- return isNil(prop('contactGroup', values))
- ? not(isNil(rowValues))
- : isNil(tableValues) ||
- isEmpty(rowValues?.claimValue) ||
- isNil(rowValues?.accessGroup);
- },
},
label: labelDefineRelationAuthorizationValueAndAccessGroup,
type: InputType.FieldsTable,
diff --git a/www/front_src/src/Authentication/Openid/index.test.tsx b/www/front_src/src/Authentication/Openid/index.test.tsx
index d7fffde1689..ab9b4efb751 100644
--- a/www/front_src/src/Authentication/Openid/index.test.tsx
+++ b/www/front_src/src/Authentication/Openid/index.test.tsx
@@ -445,11 +445,10 @@ describe('Openid configuration form', () => {
accessGroupsEndpoint,
labelAccessGroup,
'Access Group 2',
- 1,
],
])(
'updates the %p field when an option is selected from the retrieved options',
- async (_, retrievedOptions, endpoint, label, value, index = 0) => {
+ async (_, retrievedOptions, endpoint, label, value) => {
mockGetRequestsWithNoAuthorizationConfiguration();
renderOpenidConfigurationForm();
@@ -484,7 +483,7 @@ describe('Openid configuration form', () => {
userEvent.click(screen.getByText(value));
await waitFor(() => {
- expect(screen.getAllByLabelText(label)[index]).toHaveValue(value);
+ expect(screen.getAllByLabelText(label)[0]).toHaveValue(value);
});
},
);
@@ -508,40 +507,4 @@ describe('Openid configuration form', () => {
);
});
});
-
- it('displays the "Authorization value" and "Access group" fields as required when the "Contact group" field is filled', async () => {
- mockGetRequestsWithNoAuthorizationConfiguration();
- mockedAxios.get.mockResolvedValueOnce({
- data: retrievedContactGroups,
- });
-
- renderOpenidConfigurationForm();
-
- await waitFor(() => {
- expect(screen.getByLabelText(labelContactGroup)).toBeInTheDocument();
- });
-
- userEvent.click(screen.getByLabelText(labelContactGroup));
-
- await waitFor(() => {
- expect(mockedAxios.get).toHaveBeenCalledWith(
- `${contactGroupsEndpoint}?page=1&sort_by=${encodeURIComponent(
- '{"name":"ASC"}',
- )}`,
- cancelTokenRequestParam,
- );
- });
-
- await waitFor(() => {
- expect(screen.getByText('Contact Group 1')).toBeInTheDocument();
- });
-
- userEvent.click(screen.getByText('Contact Group 1'));
-
- await waitFor(() => {
- expect(screen.getByLabelText(labelAuthorizationValue)).toHaveAttribute(
- 'required',
- );
- });
- });
});
diff --git a/www/front_src/src/Authentication/Openid/useValidationSchema.ts b/www/front_src/src/Authentication/Openid/useValidationSchema.ts
index 4f703165f68..038812dadee 100644
--- a/www/front_src/src/Authentication/Openid/useValidationSchema.ts
+++ b/www/front_src/src/Authentication/Openid/useValidationSchema.ts
@@ -6,7 +6,6 @@ import {
labelRequired,
labelInvalidURL,
labelInvalidIPAddress,
- labelAtLeastOneAuthorizationIsRequired,
} from './translatedLabels';
const IPAddressRegexp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,3})?$/;
@@ -29,15 +28,7 @@ const useValidationSchema = (): Yup.SchemaOf => {
return Yup.object({
authenticationType: Yup.string().required(t(labelRequired)),
authorizationEndpoint: Yup.string().nullable().required(t(labelRequired)),
- authorizationRules: Yup.array()
- .of(authorizationSchema)
- .when('contactGroup', (contactGroup, schema) => {
- return contactGroup
- ? schema
- .min(1, t(labelAtLeastOneAuthorizationIsRequired))
- .required(t(labelRequired))
- : schema.nullable();
- }),
+ authorizationRules: Yup.array().of(authorizationSchema),
autoImport: Yup.boolean().required(t(labelRequired)),
baseUrl: Yup.string()
.matches(urlRegexp, t(labelInvalidURL))
diff --git a/www/front_src/src/Authentication/index.tsx b/www/front_src/src/Authentication/index.tsx
index bf12422f160..71668d687f8 100644
--- a/www/front_src/src/Authentication/index.tsx
+++ b/www/front_src/src/Authentication/index.tsx
@@ -1,9 +1,8 @@
-import { useMemo } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
import { useUpdateAtom } from 'jotai/utils';
-import { Responsive } from '@visx/visx';
import { Box, Container, Paper, Tab } from '@mui/material';
import { TabContext, TabList, TabPanel } from '@mui/lab';
@@ -90,35 +89,39 @@ const useStyles = makeStyles((theme) => ({
formContainer: {
display: 'grid',
gridTemplateColumns: '1.2fr 0.6fr',
- height: '100%',
- overflowY: 'auto',
+ justifyItems: 'center',
padding: theme.spacing(3),
},
image: {
+ height: '200px',
opacity: 0.5,
padding: theme.spacing(0, 5),
position: 'sticky',
top: 0,
+ width: '200px',
},
panel: {
- height: '80%',
padding: 0,
},
paper: {
boxShadow: theme.shadows[3],
- height: '100%',
},
tabList: {
boxShadow: theme.shadows[2],
},
}));
-const marginBottomHeight = 88;
+const scrollMargin = 8;
const Authentication = (): JSX.Element => {
const classes = useStyles();
const { t } = useTranslation();
+ const formContainerRef = useRef(null);
+
+ const [windowHeight, setWindowHeight] = useState(window.innerHeight);
+ const [clientRect, setClientRect] = useState(null);
+
const appliedTab = useAtomValue(appliedTabAtom);
const { themeMode } = useAtomValue(userAtom);
const setTab = useUpdateAtom(tabAtom);
@@ -127,6 +130,23 @@ const Authentication = (): JSX.Element => {
setTab(newTab);
};
+ const resize = (): void => {
+ setWindowHeight(window.innerHeight);
+ };
+
+ useEffect(() => {
+ window.addEventListener('resize', resize);
+
+ setClientRect(formContainerRef.current?.getBoundingClientRect() ?? null);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ };
+ }, []);
+
+ const formContainerHeight =
+ windowHeight - (clientRect?.top || 0) - scrollMargin;
+
const tabs = useMemo(
() =>
panels.map(({ title, value }) => (
@@ -136,33 +156,31 @@ const Authentication = (): JSX.Element => {
);
const tabPanels = useMemo(
- () => (
-
- {({ height }): Array =>
- panels.map(({ Component, value, image }) => (
-
-
-
-
-
-
- ))
- }
-
- ),
- [themeMode],
+ () =>
+ panels.map(({ Component, value, image }) => (
+
+
+
+
+
+
+
+
+ )),
+ [themeMode, formContainerHeight],
);
return (
-
+
Date: Fri, 29 Jul 2022 11:52:57 +0100
Subject: [PATCH 11/18] [SNYK] Sanitize and bind centreonGraph class queries
(#11409) (#11421)
1122
1153
1134
---
www/class/centreonGraph.class.php | 32 ++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/www/class/centreonGraph.class.php b/www/class/centreonGraph.class.php
index fd55822883a..4ae3ef297d8 100644
--- a/www/class/centreonGraph.class.php
+++ b/www/class/centreonGraph.class.php
@@ -1076,16 +1076,18 @@ private function getDefaultGraphTemplate()
return;
} else {
$command_id = getMyServiceField($this->indexData["service_id"], "command_command_id");
- $DBRESULT = $this->DB->query("SELECT graph_id FROM command WHERE `command_id` = '" . $command_id . "'");
- if ($DBRESULT->rowCount()) {
- $data = $DBRESULT->fetch();
+ $statement = $this->DB->prepare("SELECT graph_id FROM command WHERE `command_id` = :command_id");
+ $statement->bindValue(':command_id', (int) $command_id, \PDO::PARAM_INT);
+ $statement->execute();
+ if ($statement->rowCount()) {
+ $data = $statement->fetch();
if ($data["graph_id"] != 0) {
$this->templateId = $data["graph_id"];
unset($data);
return;
}
}
- $DBRESULT->closeCursor();
+ $statement->closeCursor();
unset($command_id);
}
$DBRESULT = $this->DB->query("SELECT graph_id FROM giv_graphs_template WHERE default_tpl1 = '1' LIMIT 1");
@@ -1119,12 +1121,12 @@ public function setTemplate($template_id = null)
/*
* Graph is based on a module check point
*/
- $DBRESULT_meta = $this->DB->query(
- "SELECT graph_id
+ $statement = $this->DB->prepare("SELECT graph_id
FROM meta_service
- WHERE `meta_name` = '" . $this->indexData["service_description"] . "'"
- );
- $meta = $DBRESULT_meta->fetch();
+ WHERE `meta_name` = :service_desc");
+ $statement->bindValue(':service_desc', $this->indexData["service_description"], PDO::PARAM_STR);
+ $statement->execute();
+ $meta = $statement->fetch();
$this->templateId = $meta["graph_id"];
unset($meta);
}
@@ -1149,14 +1151,14 @@ private function getServiceGraphID()
$service_id = $this->indexData["service_id"];
$tab = array();
- while (1) {
- $DBRESULT = $this->DB->query(
- "SELECT esi.graph_id, service_template_model_stm_id
+ $statement = $this->DB->prepare("SELECT esi.graph_id, service_template_model_stm_id
FROM service
LEFT JOIN extended_service_information esi ON esi.service_service_id = service_id
- WHERE service_id = '" . $service_id . "' LIMIT 1"
- );
- $row = $DBRESULT->fetch();
+ WHERE service_id = :service_id LIMIT 1");
+ while (1) {
+ $statement->bindValue(':service_id', (int) $service_id, \PDO::PARAM_INT);
+ $statement->execute();
+ $row = $statement->fetch();
if ($row["graph_id"]) {
$this->graphID = $row["graph_id"];
return $this->graphID;
From b524e4a228e6973935ce2e07b90c8955c0a1cbc9 Mon Sep 17 00:00:00 2001
From: Elmahdi ABBASSI <108519266+emabassi-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 11:53:08 +0100
Subject: [PATCH 12/18] [Snyk] Sanitize and bind ACL action access queries
(#11385) (#11402)
* Sanitize and bind ACL action access queries
_ sanitize if possible each variables inserted in a query
_ use PDO prepared statement and bind() method
_ Do not use $pearDB->escape on which is for examples useless on integers and on non closed HTML tags (svg, img, etc)
* fix line length
* fix failed checks
---
.../accessLists/menusACL/formMenusAccess.php | 31 ++++++++++++-------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/www/include/options/accessLists/menusACL/formMenusAccess.php b/www/include/options/accessLists/menusACL/formMenusAccess.php
index 1939b23271c..6704e8b7ce8 100644
--- a/www/include/options/accessLists/menusACL/formMenusAccess.php
+++ b/www/include/options/accessLists/menusACL/formMenusAccess.php
@@ -209,9 +209,12 @@
$b = 0;
$query = "SELECT topology_id, topology_page, topology_name, topology_parent, readonly FROM topology " .
- "WHERE topology_parent = '" . $topo1["topology_page"] . "' ORDER BY topology_order";
- $DBRESULT2 = $pearDB->query($query);
- while ($topo2 = $DBRESULT2->fetchRow()) {
+ "WHERE topology_parent = :topology_parent ORDER BY topology_order";
+
+ $statement2 = $pearDB->prepare($query);
+ $statement2->bindValue(':topology_parent', (int) $topo1["topology_page"], \PDO::PARAM_INT);
+ $statement2->execute();
+ while ($topo2 = $statement2->fetchRow()) {
$acl_topos2[$a]["childs"][$b] = array();
$acl_topos2[$a]["childs"][$b]["name"] = _($topo2["topology_name"]);
$acl_topos2[$a]["childs"][$b]["id"] = $topo2["topology_id"];
@@ -231,10 +234,14 @@
$c = 0;
$query = "SELECT topology_id, topology_name, topology_parent, topology_page, topology_group, readonly " .
- "FROM topology WHERE topology_parent = '" . $topo2["topology_page"] .
- "' AND topology_page IS NOT NULL ORDER BY topology_group, topology_order";
- $DBRESULT3 = $pearDB->query($query);
- while ($topo3 = $DBRESULT3->fetchRow()) {
+ "FROM topology WHERE topology_parent = :topology_parent " .
+ "AND topology_page IS NOT NULL ORDER BY topology_group, topology_order";
+
+ $statement3 = $pearDB->prepare($query);
+ $statement3->bindValue(':topology_parent', (int) $topo2["topology_page"], \PDO::PARAM_INT);
+ $statement3->execute();
+
+ while ($topo3 = $statement3->fetchRow()) {
$acl_topos2[$a]["childs"][$b]["childs"][$c] = array();
$acl_topos2[$a]["childs"][$b]["childs"][$c]["name"] = _($topo3["topology_name"]);
@@ -264,10 +271,12 @@
$d = 0;
$query = "SELECT topology_id, topology_name, topology_parent, readonly FROM topology " .
- "WHERE topology_parent = '" . $topo3["topology_page"] .
- "' AND topology_page IS NOT NULL ORDER BY topology_order";
- $DBRESULT4 = $pearDB->query($query);
- while ($topo4 = $DBRESULT4->fetchRow()) {
+ "WHERE topology_parent = :topology_parent AND topology_page IS NOT NULL ORDER BY topology_order";
+ $statement4 = $pearDB->prepare($query);
+ $statement4->bindValue(':topology_parent', (int) $topo3["topology_page"], \PDO::PARAM_INT);
+ $statement4->execute();
+
+ while ($topo4 = $statement4->fetchRow()) {
$acl_topos2[$a]["childs"][$b]["childs"][$c]["childs"][$d] = array();
$acl_topos2[$a]["childs"][$b]["childs"][$c]["childs"][$d]["name"] = _($topo4["topology_name"]);
$acl_topos2[$a]["childs"][$b]["childs"][$c]["childs"][$d]["id"] = $topo4["topology_id"];
From b04ce8a3c26cc9749ef1ba690f70939fe68f156d Mon Sep 17 00:00:00 2001
From: Kevin Duret
Date: Fri, 29 Jul 2022 14:28:46 +0200
Subject: [PATCH 13/18] feat(api): implement endpoint to update centreon web
(#11391) (#11401)
Refs: MON-12296
---
ci/debian/centreon-web.postinst | 6 +
composer.json | 1 +
composer.lock | 83 ++++-
config/packages/Centreon.yaml | 40 +++
config/routes/Centreon/platform.yaml | 6 +
config/services.yaml | 5 +
doc/API/centreon-api-v22.04.yaml | 2 +
doc/API/v22.04/Administration/updates.yaml | 30 ++
lang/fr_FR.UTF-8/LC_MESSAGES/messages.po | 54 +++
.../Infrastructure/DatabaseConnection.php | 10 +
.../Repository/AbstractRepositoryDRB.php | 4 +-
.../ReadUpdateRepositoryInterface.php | 34 ++
.../ReadVersionRepositoryInterface.php | 33 ++
.../Repository/UpdateLockerException.php | 44 +++
.../UpdateLockerRepositoryInterface.php | 42 +++
.../Repository/UpdateNotFoundException.php | 36 ++
.../WriteUpdateRepositoryInterface.php | 40 +++
.../UseCase/UpdateVersions/UpdateVersions.php | 219 ++++++++++++
.../UpdateVersionsException.php | 87 +++++
.../UpdateVersionsPresenterInterface.php | 29 ++
.../Validator/RequirementException.php | 27 ++
.../RequirementValidatorInterface.php | 33 ++
.../RequirementValidatorsInterface.php | 33 ++
.../UpdateVersionsController.php | 70 ++++
.../UpdateVersionsPresenter.php | 31 ++
.../UpdateVersions/UpdateVersionsSchema.json | 25 ++
.../Repository/DbReadVersionRepository.php | 60 ++++
.../Repository/DbWriteUpdateRepository.php | 319 ++++++++++++++++++
.../Repository/FsReadUpdateRepository.php | 105 ++++++
.../SymfonyUpdateLockerRepository.php | 79 +++++
.../Validator/RequirementValidators.php | 63 ++++
.../DatabaseRequirementException.php | 49 +++
.../DatabaseRequirementValidator.php | 128 +++++++
.../DatabaseRequirementValidatorInterface.php | 43 +++
.../MariaDbRequirementException.php | 44 +++
.../MariaDbRequirementValidator.php | 82 +++++
.../PhpRequirementException.php | 56 +++
.../PhpRequirementValidator.php | 108 ++++++
tests/api/Context/PlatformUpdateContext.php | 48 +++
tests/api/behat.yml | 4 +
tests/api/features/PlatformUpdate.feature | 41 +++
.../UpdateVersions/UpdateVersionsTest.php | 157 +++++++++
.../Repository/FsReadUpdateRepositoryTest.php | 89 +++++
tests/php/bootstrap.php | 3 +-
.../step_upgrade/process/process_step4.php | 134 ++------
.../step_upgrade/process/process_step5.php | 58 +---
46 files changed, 2546 insertions(+), 148 deletions(-)
create mode 100644 doc/API/v22.04/Administration/updates.yaml
create mode 100644 src/Core/Platform/Application/Repository/ReadUpdateRepositoryInterface.php
create mode 100644 src/Core/Platform/Application/Repository/ReadVersionRepositoryInterface.php
create mode 100644 src/Core/Platform/Application/Repository/UpdateLockerException.php
create mode 100644 src/Core/Platform/Application/Repository/UpdateLockerRepositoryInterface.php
create mode 100644 src/Core/Platform/Application/Repository/UpdateNotFoundException.php
create mode 100644 src/Core/Platform/Application/Repository/WriteUpdateRepositoryInterface.php
create mode 100644 src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersions.php
create mode 100644 src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsException.php
create mode 100644 src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsPresenterInterface.php
create mode 100644 src/Core/Platform/Application/Validator/RequirementException.php
create mode 100644 src/Core/Platform/Application/Validator/RequirementValidatorInterface.php
create mode 100644 src/Core/Platform/Application/Validator/RequirementValidatorsInterface.php
create mode 100644 src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsController.php
create mode 100644 src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsPresenter.php
create mode 100644 src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsSchema.json
create mode 100644 src/Core/Platform/Infrastructure/Repository/DbReadVersionRepository.php
create mode 100644 src/Core/Platform/Infrastructure/Repository/DbWriteUpdateRepository.php
create mode 100644 src/Core/Platform/Infrastructure/Repository/FsReadUpdateRepository.php
create mode 100644 src/Core/Platform/Infrastructure/Repository/SymfonyUpdateLockerRepository.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementException.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidator.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidatorInterface.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidators/MariaDbRequirementException.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidators/MariaDbRequirementValidator.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/PhpRequirementException.php
create mode 100644 src/Core/Platform/Infrastructure/Validator/RequirementValidators/PhpRequirementValidator.php
create mode 100644 tests/api/Context/PlatformUpdateContext.php
create mode 100644 tests/api/features/PlatformUpdate.feature
create mode 100644 tests/php/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsTest.php
create mode 100644 tests/php/Core/Platform/Infrastructure/Repository/FsReadUpdateRepositoryTest.php
diff --git a/ci/debian/centreon-web.postinst b/ci/debian/centreon-web.postinst
index 59f1d40c7ac..8a557942674 100644
--- a/ci/debian/centreon-web.postinst
+++ b/ci/debian/centreon-web.postinst
@@ -57,4 +57,10 @@ if [ "$1" = "configure" ] ; then
fi
fi
+
+# rebuild symfony cache on upgrade
+if [ -n "$2" ]; then
+ su - www-data -s /bin/bash -c "/usr/share/centreon/bin/console cache:clear --no-warmup"
+fi
+
exit 0
diff --git a/composer.json b/composer.json
index c7c1a574bcd..e87c05a183d 100644
--- a/composer.json
+++ b/composer.json
@@ -65,6 +65,7 @@
"symfony/framework-bundle": "5.4.*",
"symfony/http-client": "5.4.*",
"symfony/http-kernel": "5.4.*",
+ "symfony/lock": "5.4.*",
"symfony/maker-bundle": "^1.11",
"symfony/monolog-bundle": "^3.7",
"symfony/options-resolver": "5.4.*",
diff --git a/composer.lock b/composer.lock
index 23c9b05bd79..6dc53f71f39 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "94134d5a5dc2cb311e57863a9f0dafd8",
+ "content-hash": "1c4ccf2b3d1f768dfd30298637120a27",
"packages": [
{
"name": "beberlei/assert",
@@ -3623,6 +3623,85 @@
],
"time": "2022-05-27T07:09:08+00:00"
},
+ {
+ "name": "symfony/lock",
+ "version": "v5.4.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/lock.git",
+ "reference": "41a308008d92d30cae5615d903c4d46d95932eea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/lock/zipball/41a308008d92d30cae5615d903c4d46d95932eea",
+ "reference": "41a308008d92d30cae5615d903c4d46d95932eea",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "doctrine/dbal": "<2.13"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^2.13|^3.0",
+ "predis/predis": "~1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Lock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jérémy Derussé",
+ "email": "jeremy@derusse.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cas",
+ "flock",
+ "locking",
+ "mutex",
+ "redlock",
+ "semaphore"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/lock/tree/v5.4.10"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-06-09T13:29:56+00:00"
+ },
{
"name": "symfony/maker-bundle",
"version": "v1.43.0",
@@ -10885,5 +10964,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.3.0"
}
diff --git a/config/packages/Centreon.yaml b/config/packages/Centreon.yaml
index f0d99f90221..f8efbb150f4 100644
--- a/config/packages/Centreon.yaml
+++ b/config/packages/Centreon.yaml
@@ -223,6 +223,42 @@ services:
class: Core\Infrastructure\Platform\Repository\FileReadPlatformRepository
arguments: ['%centreon_etc_path%', '%centreon_install_path%']
+ Core\Platform\Application\Validator\RequirementValidatorsInterface:
+ class: Core\Platform\Infrastructure\Validator\RequirementValidators
+ arguments:
+ $requirementValidators: !tagged_iterator 'platform.requirement.validators'
+
+ Core\Platform\Infrastructure\Validator\RequirementValidators\DatabaseRequirementValidator:
+ arguments:
+ $dbRequirementValidators: !tagged_iterator 'platform.requirement.database.validators'
+
+ Core\Platform\Infrastructure\Validator\RequirementValidators\PhpRequirementValidator:
+ arguments:
+ $requiredPhpVersion: '%required_php_version%'
+
+ Core\Platform\Infrastructure\Validator\RequirementValidators\DatabaseRequirementValidators\MariaDbRequirementValidator:
+ arguments:
+ $requiredMariaDbMinVersion: '%required_mariadb_min_version%'
+
+ Core\Platform\Application\Repository\ReadVersionRepositoryInterface:
+ class: Core\Platform\Infrastructure\Repository\DbReadVersionRepository
+ public: true
+
+ Core\Platform\Application\Repository\ReadUpdateRepositoryInterface:
+ class: Core\Platform\Infrastructure\Repository\FsReadUpdateRepository
+ arguments:
+ $installDir: '%centreon_install_path%'
+ public: true
+
+ Core\Platform\Application\Repository\UpdateLockerRepositoryInterface:
+ class: Core\Platform\Infrastructure\Repository\SymfonyUpdateLockerRepository
+ public: true
+
+ Core\Platform\Application\Repository\WriteUpdateRepositoryInterface:
+ class: Core\Platform\Infrastructure\Repository\DbWriteUpdateRepository
+ arguments: ['%centreon_var_lib%', '%centreon_install_path%']
+ public: true
+
# Monitoring resources
_instanceof:
Centreon\Infrastructure\Monitoring\Resource\Provider\ProviderInterface:
@@ -238,6 +274,10 @@ services:
tags: ['authentication.provider.responses']
Core\Security\Infrastructure\Api\FindProviderConfigurations\ProviderPresenter\ProviderPresenterInterface:
tags: ['authentication.provider.presenters']
+ Core\Platform\Application\Validator\RequirementValidatorInterface:
+ tags: ['platform.requirement.validators']
+ Core\Platform\Infrastructure\Validator\RequirementValidators\DatabaseRequirementValidatorInterface:
+ tags: ['platform.requirement.database.validators']
Centreon\Domain\Monitoring\Interfaces\ResourceRepositoryInterface:
factory: ['@Centreon\Infrastructure\Monitoring\Resource\ResourceRepositoryFactory', 'createResourceRepository']
diff --git a/config/routes/Centreon/platform.yaml b/config/routes/Centreon/platform.yaml
index a521348ecbf..d77666e43f6 100644
--- a/config/routes/Centreon/platform.yaml
+++ b/config/routes/Centreon/platform.yaml
@@ -4,6 +4,12 @@ centreon_application_platform_getversion:
controller: 'Centreon\Application\Controller\PlatformController::getVersions'
condition: "request.attributes.get('version') >= 21.10"
+centreon_application_platform_updateversions:
+ methods: PATCH
+ path: /platform/updates
+ controller: 'Core\Platform\Infrastructure\Api\UpdateVersions\UpdateVersionsController'
+ condition: "request.attributes.get('version') >= 22.04"
+
centreon_application_platformtopology_addplatformtotopology:
methods: POST
path: /platform/topology
diff --git a/config/services.yaml b/config/services.yaml
index 41975cd9de1..566596ebc51 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -22,6 +22,8 @@ parameters:
media_path: "img/media"
redirect_default_page: "/monitoring/resources"
session_expiration_delay: 120
+ required_php_version: "%env(_CENTREON_PHP_VERSION_)%"
+ required_mariadb_min_version: "%env(_CENTREON_MARIA_DB_MIN_VERSION_)%"
services:
# Default configuration for services in *this* file
@@ -66,6 +68,9 @@ services:
decorates: router
arguments: ['@.inner']
+ Symfony\Component\Finder\Finder:
+ shared: false
+
# Security
Security\Domain\Authentication\Interfaces\AuthenticationRepositoryInterface:
diff --git a/doc/API/centreon-api-v22.04.yaml b/doc/API/centreon-api-v22.04.yaml
index a75b7a57821..bb949d8c36b 100644
--- a/doc/API/centreon-api-v22.04.yaml
+++ b/doc/API/centreon-api-v22.04.yaml
@@ -3528,6 +3528,8 @@ paths:
moduleName:
type: object
$ref: '#/components/schemas/Platform.Versions'
+ /platform/updates:
+ $ref: "./v22.04/Administration/updates.yaml"
/platform/installation/status:
get:
tags:
diff --git a/doc/API/v22.04/Administration/updates.yaml b/doc/API/v22.04/Administration/updates.yaml
new file mode 100644
index 00000000000..58e895903c1
--- /dev/null
+++ b/doc/API/v22.04/Administration/updates.yaml
@@ -0,0 +1,30 @@
+---
+patch:
+ tags:
+ - Platform
+ summary: "Update Centreon web"
+ description: |
+ Update Centreon web component
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ components:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ enum: [ centreon-web ]
+ responses:
+ 204:
+ description: "Platform updated"
+ 404:
+ description: "Updates not found"
+ 500:
+ $ref: "../../centreon-api-v22.04.yaml#/components/responses/InternalServerError"
+...
\ No newline at end of file
diff --git a/lang/fr_FR.UTF-8/LC_MESSAGES/messages.po b/lang/fr_FR.UTF-8/LC_MESSAGES/messages.po
index 1c1b0bbed83..73a58b0f385 100644
--- a/lang/fr_FR.UTF-8/LC_MESSAGES/messages.po
+++ b/lang/fr_FR.UTF-8/LC_MESSAGES/messages.po
@@ -16914,3 +16914,57 @@ msgstr "Les attributs liés suivants sont manquants : %s"
msgid "Warning, maximum size exceeded for input '%s' (max: %d), it will be truncated upon saving"
msgstr "Attention, taille maximale dépassée pour le champ '%s' (max: %d), il sera tronqué à l'enregistrement"
+
+msgid "Update already in progress"
+msgstr "Une mise à jour est déjà en cours"
+
+msgid "An error occurred when retrieving the current version"
+msgstr "Une erreur s'est produite lors de la récupération de la version actuelle"
+
+msgid "Cannot retrieve the current version"
+msgstr "La version actuelle n'a pas pu être récupérée"
+
+msgid "An error occurred when retrieving available updates"
+msgstr "Une erreur s'est produite lors de la récupération des mises à jour disponibles"
+
+msgid "An error occurred when applying the update %s (%s)"
+msgstr "Une erreur s'est produite lors de l'application de la mise à jour %s (%s)"
+
+msgid "Error while locking the update process"
+msgstr "Erreur lors du verrouillage du processus de mise à jour"
+
+msgid "Error while unlocking the update process"
+msgstr "Erreur lors du déverrouillage du processus de mise à jour"
+
+msgid "An error occurred when applying post update actions"
+msgstr "Une erreur s'est produite lors de l'application des actions postérieures à la mise à jour"
+
+msgid "Updates not found"
+msgstr "Les mises à jour n'ont pas été trouvées"
+
+msgid "PHP version %s required (%s installed)"
+msgstr "La version %s de PHP est requise (%s installée)"
+
+msgid "PHP extension %s not loaded"
+msgstr "L'extension %s de PHP n'est pas chargée"
+
+msgid "Error when retrieving the database version"
+msgstr "Erreur lors de la récupération de la version de la base de données"
+
+msgid "Cannot retrieve the database version information"
+msgstr "Les informations de version de la base de données n'ont pas pu être récupérées"
+
+msgid "MariaDB version %s required (%s installed)"
+msgstr "La version %s de MariaDB est requise (%s installée)"
+
+msgid "Service severity"
+msgstr "Criticité du service"
+
+msgid "Service severity level"
+msgstr "Niveau de criticité du service"
+
+msgid "Host severity"
+msgstr "Criticité d'hôte"
+
+msgid "Host severity level"
+msgstr "Niveau de criticité d'hôte"
diff --git a/src/Centreon/Infrastructure/DatabaseConnection.php b/src/Centreon/Infrastructure/DatabaseConnection.php
index 404ada96717..39263cc0cff 100644
--- a/src/Centreon/Infrastructure/DatabaseConnection.php
+++ b/src/Centreon/Infrastructure/DatabaseConnection.php
@@ -91,4 +91,14 @@ public function setStorageDbName(string $storageDbName)
{
$this->storageDbName = $storageDbName;
}
+
+ /**
+ * switch connection to another database
+ *
+ * @param string $dbName
+ */
+ public function switchToDb(string $dbName): void
+ {
+ $this->query('use ' . $dbName);
+ }
}
diff --git a/src/Centreon/Infrastructure/Repository/AbstractRepositoryDRB.php b/src/Centreon/Infrastructure/Repository/AbstractRepositoryDRB.php
index c8ccf79ea23..27e68c256fd 100644
--- a/src/Centreon/Infrastructure/Repository/AbstractRepositoryDRB.php
+++ b/src/Centreon/Infrastructure/Repository/AbstractRepositoryDRB.php
@@ -48,8 +48,8 @@ class AbstractRepositoryDRB
protected function translateDbName(string $request): string
{
return str_replace(
- array(':dbstg', ':db'),
- array($this->db->getStorageDbName(), $this->db->getCentreonDbName()),
+ [':dbstg', ':db'],
+ [$this->db->getStorageDbName(), $this->db->getCentreonDbName()],
$request
);
}
diff --git a/src/Core/Platform/Application/Repository/ReadUpdateRepositoryInterface.php b/src/Core/Platform/Application/Repository/ReadUpdateRepositoryInterface.php
new file mode 100644
index 00000000000..db999e5fa71
--- /dev/null
+++ b/src/Core/Platform/Application/Repository/ReadUpdateRepositoryInterface.php
@@ -0,0 +1,34 @@
+info('Updating versions');
+
+ try {
+ $this->validateRequirementsOrFail();
+
+ $this->lockUpdate();
+
+ $currentVersion = $this->getCurrentVersionOrFail();
+
+ $availableUpdates = $this->getAvailableUpdatesOrFail($currentVersion);
+
+ $this->runUpdates($availableUpdates);
+
+ $this->unlockUpdate();
+
+ $this->runPostUpdate($this->getCurrentVersionOrFail());
+ } catch (UpdateNotFoundException $e) {
+ $this->error(
+ $e->getMessage(),
+ ['trace' => $e->getTraceAsString()],
+ );
+
+ $presenter->setResponseStatus(new NotFoundResponse('Updates'));
+
+ return;
+ } catch (\Throwable $e) {
+ $this->error(
+ $e->getMessage(),
+ ['trace' => $e->getTraceAsString()],
+ );
+
+ $presenter->setResponseStatus(new ErrorResponse($e->getMessage()));
+
+ return;
+ }
+
+ $presenter->setResponseStatus(new NoContentResponse());
+ }
+
+ /**
+ * Validate platform requirements or fail
+ *
+ * @throws \Exception
+ */
+ private function validateRequirementsOrFail(): void
+ {
+ $this->info('Validating platform requirements');
+
+ $this->requirementValidators->validateRequirementsOrFail();
+ }
+
+ /**
+ * Lock update process
+ */
+ private function lockUpdate(): void
+ {
+ $this->info('Locking centreon update process...');
+
+ if (!$this->updateLocker->lock()) {
+ throw UpdateVersionsException::updateAlreadyInProgress();
+ }
+ }
+
+ /**
+ * Unlock update process
+ */
+ private function unlockUpdate(): void
+ {
+ $this->info('Unlocking centreon update process...');
+
+ $this->updateLocker->unlock();
+ }
+
+ /**
+ * Get current version or fail
+ *
+ * @return string
+ *
+ * @throws \Exception
+ */
+ private function getCurrentVersionOrFail(): string
+ {
+ $this->info('Getting current version');
+
+ try {
+ $currentVersion = $this->readVersionRepository->findCurrentVersion();
+ } catch (\Exception $e) {
+ throw UpdateVersionsException::errorWhenRetrievingCurrentVersion($e);
+ }
+
+ if ($currentVersion === null) {
+ throw UpdateVersionsException::cannotRetrieveCurrentVersion();
+ }
+
+ return $currentVersion;
+ }
+
+ /**
+ * Get available updates
+ *
+ * @param string $currentVersion
+ * @return string[]
+ */
+ private function getAvailableUpdatesOrFail(string $currentVersion): array
+ {
+ try {
+ $this->info(
+ 'Getting available updates',
+ [
+ 'current_version' => $currentVersion,
+ ],
+ );
+
+ return $this->readUpdateRepository->findOrderedAvailableUpdates($currentVersion);
+ } catch (UpdateNotFoundException $e) {
+ throw $e;
+ } catch (\Throwable $e) {
+ throw UpdateVersionsException::errorWhenRetrievingAvailableUpdates($e);
+ }
+ }
+
+ /**
+ * Run given version updates
+ *
+ * @param string[] $versions
+ *
+ * @throws \Throwable
+ */
+ private function runUpdates(array $versions): void
+ {
+ foreach ($versions as $version) {
+ try {
+ $this->info("Running update $version");
+ $this->writeUpdateRepository->runUpdate($version);
+ } catch (\Throwable $e) {
+ throw UpdateVersionsException::errorWhenApplyingUpdate($version, $e->getMessage(), $e);
+ }
+ }
+ }
+
+ /**
+ * Run post update actions
+ *
+ * @param string $currentVersion
+ *
+ * @throws UpdateVersionsException
+ */
+ private function runPostUpdate(string $currentVersion): void
+ {
+ $this->info("Running post update actions");
+
+ try {
+ $this->writeUpdateRepository->runPostUpdate($currentVersion);
+ } catch (\Throwable $e) {
+ throw UpdateVersionsException::errorWhenApplyingPostUpdate($e);
+ }
+ }
+}
diff --git a/src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsException.php b/src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsException.php
new file mode 100644
index 00000000000..dbfaec97ba3
--- /dev/null
+++ b/src/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsException.php
@@ -0,0 +1,87 @@
+denyAccessUnlessGrantedForApiConfiguration();
+
+ /**
+ * @var Contact $contact
+ */
+ $contact = $this->getUser();
+ if (! $contact->isAdmin()) {
+ $presenter->setResponseStatus(new UnauthorizedResponse('Only admin user can perform upgrade'));
+
+ return $presenter->show();
+ }
+
+ $this->info('Validating request body...');
+ $this->validateDataSent($request, __DIR__ . '/UpdateVersionsSchema.json');
+
+ $useCase($presenter);
+
+ return $presenter->show();
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsPresenter.php b/src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsPresenter.php
new file mode 100644
index 00000000000..a27dcfad745
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Api/UpdateVersions/UpdateVersionsPresenter.php
@@ -0,0 +1,31 @@
+db = $db;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function findCurrentVersion(): ?string
+ {
+ $currentVersion = null;
+
+ $statement = $this->db->query(
+ "SELECT `value` FROM `informations` WHERE `key` = 'version'"
+ );
+ if ($statement !== false && is_array($result = $statement->fetch(\PDO::FETCH_ASSOC))) {
+ $currentVersion = $result['value'];
+ }
+
+ return $currentVersion;
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Repository/DbWriteUpdateRepository.php b/src/Core/Platform/Infrastructure/Repository/DbWriteUpdateRepository.php
new file mode 100644
index 00000000000..1255ee9ecc8
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Repository/DbWriteUpdateRepository.php
@@ -0,0 +1,319 @@
+db = $db;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function runUpdate(string $version): void
+ {
+ $this->runMonitoringSql($version);
+ $this->runScript($version);
+ $this->runConfigurationSql($version);
+ $this->runPostScript($version);
+ $this->updateVersionInformation($version);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function runPostUpdate(string $currentVersion): void
+ {
+ if (! $this->filesystem->exists($this->installDir)) {
+ return;
+ }
+
+ $this->backupInstallDirectory($currentVersion);
+ $this->removeInstallDirectory();
+ }
+
+ /**
+ * Backup installation directory
+ *
+ * @param string $currentVersion
+ */
+ private function backupInstallDirectory(string $currentVersion): void
+ {
+ $backupDirectory = $this->libDir . '/installs/install-' . $currentVersion . '-' . date('Ymd_His');
+
+ $this->info(
+ "Backing up installation directory",
+ [
+ 'source' => $this->installDir,
+ 'destination' => $backupDirectory,
+ ],
+ );
+
+ $this->filesystem->mirror(
+ $this->installDir,
+ $backupDirectory,
+ );
+ }
+
+ /**
+ * Remove installation directory
+ */
+ private function removeInstallDirectory(): void
+ {
+ $this->info(
+ "Removing installation directory",
+ [
+ 'installation_directory' => $this->installDir,
+ ],
+ );
+
+ $this->filesystem->remove($this->installDir);
+ }
+
+ /**
+ * Run sql queries on monitoring database
+ *
+ * @param string $version
+ */
+ private function runMonitoringSql(string $version): void
+ {
+ $upgradeFilePath = $this->installDir . '/sql/centstorage/Update-CSTG-' . $version . '.sql';
+ if (is_readable($upgradeFilePath)) {
+ $this->db->switchToDb($this->db->getStorageDbName());
+ $this->runSqlFile($upgradeFilePath);
+ }
+ }
+
+ /**
+ * Run php upgrade script
+ *
+ * @param string $version
+ */
+ private function runScript(string $version): void
+ {
+ $pearDB = $this->dependencyInjector['configuration_db'];
+ $pearDBO = $this->dependencyInjector['realtime_db'];
+
+ $upgradeFilePath = $this->installDir . '/php/Update-' . $version . '.php';
+ if (is_readable($upgradeFilePath)) {
+ include_once $upgradeFilePath;
+ }
+ }
+
+ /**
+ * Run sql queries on configuration database
+ *
+ * @param string $version
+ */
+ private function runConfigurationSql(string $version): void
+ {
+ $upgradeFilePath = $this->installDir . '/sql/centreon/Update-DB-' . $version . '.sql';
+ if (is_readable($upgradeFilePath)) {
+ $this->db->switchToDb($this->db->getCentreonDbName());
+ $this->runSqlFile($upgradeFilePath);
+ }
+ }
+
+ /**
+ * Run php post upgrade script
+ *
+ * @param string $version
+ */
+ private function runPostScript(string $version): void
+ {
+ $pearDB = $this->dependencyInjector['configuration_db'];
+ $pearDBO = $this->dependencyInjector['realtime_db'];
+
+ $upgradeFilePath = $this->installDir . '/php/Update-' . $version . '.post.php';
+ if (is_readable($upgradeFilePath)) {
+ include_once $upgradeFilePath;
+ }
+ }
+
+ /**
+ * Update version information
+ *
+ * @param string $version
+ */
+ private function updateVersionInformation(string $version): void
+ {
+ $statement = $this->db->prepare(
+ $this->translateDbName(
+ "UPDATE `:db`.`informations` SET `value` = :version WHERE `key` = 'version'"
+ )
+ );
+ $statement->bindValue(':version', $version, \PDO::PARAM_STR);
+ $statement->execute();
+ }
+
+ /**
+ * Run sql file and use temporary file to store last executed line
+ *
+ * @param string $filePath
+ * @return void
+ */
+ private function runSqlFile(string $filePath): void
+ {
+ set_time_limit(0);
+
+ $fileName = basename($filePath);
+ $tmpFile = $this->installDir . '/tmp/' . $fileName;
+
+ $alreadyExecutedQueriesCount = $this->getAlreadyExecutedQueriesCount($tmpFile);
+
+ if (is_readable($filePath)) {
+ $fileStream = fopen($filePath, 'r');
+ if (is_resource($fileStream)) {
+ $query = '';
+ $currentLineNumber = 0;
+ $executedQueriesCount = 0;
+ try {
+ while (! feof($fileStream)) {
+ $currentLineNumber++;
+ $currentLine = fgets($fileStream);
+ if ($currentLine && ! $this->isSqlComment($currentLine)) {
+ $query .= ' ' . trim($currentLine);
+ }
+
+ if ($this->isSqlCompleteQuery($query)) {
+ $executedQueriesCount++;
+ if ($executedQueriesCount > $alreadyExecutedQueriesCount) {
+ try {
+ $this->executeQuery($query);
+ } catch (RepositoryException $e) {
+ throw $e;
+ }
+
+ $this->writeExecutedQueriesCountInTemporaryFile($tmpFile, $executedQueriesCount);
+ }
+ $query = '';
+ }
+ }
+ } catch (\Throwable $e) {
+ $this->error($e->getMessage(), ['trace' => $e->getTraceAsString()]);
+ throw $e;
+ } finally {
+ fclose($fileStream);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get stored executed queries count in temporary file to retrieve next query to run in case of an error occurred
+ *
+ * @param string $tmpFile
+ * @return int
+ */
+ private function getAlreadyExecutedQueriesCount(string $tmpFile): int
+ {
+ $startLineNumber = 0;
+ if (is_readable($tmpFile)) {
+ $lineNumber = file_get_contents($tmpFile);
+ if (is_numeric($lineNumber)) {
+ $startLineNumber = (int) $lineNumber;
+ }
+ }
+
+ return $startLineNumber;
+ }
+
+ /**
+ * Write executed queries count in temporary file to retrieve upgrade when an error occurred
+ *
+ * @param string $tmpFile
+ * @param int $count
+ */
+ private function writeExecutedQueriesCountInTemporaryFile(string $tmpFile, int $count): void
+ {
+ if (! file_exists($tmpFile) || is_writable($tmpFile)) {
+ $this->info('Writing in temporary file : ' . $tmpFile);
+ file_put_contents($tmpFile, $count);
+ } else {
+ $this->warning('Cannot write in temporary file : ' . $tmpFile);
+ }
+ }
+
+ /**
+ * Check if a line a sql comment
+ *
+ * @param string $line
+ * @return bool
+ */
+ private function isSqlComment(string $line): bool
+ {
+ return str_starts_with('--', trim($line));
+ }
+
+ /**
+ * Check if a query is complete (trailing semicolon)
+ *
+ * @param string $query
+ * @return bool
+ */
+ private function isSqlCompleteQuery(string $query): bool
+ {
+ return ! empty(trim($query)) && preg_match('/;\s*$/', $query);
+ }
+
+ /**
+ * Execute sql query
+ *
+ * @param string $query
+ *
+ * @throws \Exception
+ */
+ private function executeQuery(string $query): void
+ {
+ try {
+ $this->db->query($query);
+ } catch (\Exception $e) {
+ throw new RepositoryException('Cannot execute query: ' . $query, 0, $e);
+ }
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Repository/FsReadUpdateRepository.php b/src/Core/Platform/Infrastructure/Repository/FsReadUpdateRepository.php
new file mode 100644
index 00000000000..8c0a7916698
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Repository/FsReadUpdateRepository.php
@@ -0,0 +1,105 @@
+findAvailableUpdates($currentVersion);
+
+ return $this->orderUpdates($availableUpdates);
+ }
+
+ /**
+ * Get available updates
+ *
+ * @param string $currentVersion
+ * @return string[]
+ */
+ private function findAvailableUpdates(string $currentVersion): array
+ {
+ if (! $this->filesystem->exists($this->installDir)) {
+ $this->error('Install directory not found on filesystem: ' . $this->installDir);
+ throw UpdateNotFoundException::updatesNotFound();
+ }
+
+ $fileNameVersionRegex = '/Update-(?[a-zA-Z0-9\-\.]+)\.php/';
+ $availableUpdates = [];
+
+ $updateFiles = $this->finder->files()
+ ->in($this->installDir)
+ ->name($fileNameVersionRegex);
+
+ foreach ($updateFiles as $updateFile) {
+ if (preg_match($fileNameVersionRegex, $updateFile->getFilename(), $matches)) {
+ if (version_compare($matches['version'], $currentVersion, '>')) {
+ $this->info('Update version found: ' . $matches['version']);
+ $availableUpdates[] = $matches['version'];
+ }
+ }
+ }
+
+ return $availableUpdates;
+ }
+
+ /**
+ * Order updates
+ *
+ * @param string[] $updates
+ * @return string[]
+ */
+ private function orderUpdates(array $updates): array
+ {
+ usort(
+ $updates,
+ fn (string $versionA, string $versionB) => version_compare($versionA, $versionB),
+ );
+
+ return $updates;
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Repository/SymfonyUpdateLockerRepository.php b/src/Core/Platform/Infrastructure/Repository/SymfonyUpdateLockerRepository.php
new file mode 100644
index 00000000000..2442b6c0e0b
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Repository/SymfonyUpdateLockerRepository.php
@@ -0,0 +1,79 @@
+lock = $lockFactory->createLock(self::LOCK_NAME);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function lock(): bool
+ {
+ $this->info('Locking centreon update process on filesystem...');
+
+ try {
+ return $this->lock->acquire();
+ } catch (\Throwable $e) {
+ throw UpdateLockerException::errorWhileLockingUpdate($e);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function unlock(): void
+ {
+ $this->info('Unlocking centreon update process from filesystem...');
+
+ try {
+ $this->lock->release();
+ } catch (\Throwable $e) {
+ throw UpdateLockerException::errorWhileUnlockingUpdate($e);
+ }
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Validator/RequirementValidators.php b/src/Core/Platform/Infrastructure/Validator/RequirementValidators.php
new file mode 100644
index 00000000000..b573a47a62d
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Validator/RequirementValidators.php
@@ -0,0 +1,63 @@
+ $requirementValidators
+ *
+ * @throws \Exception
+ */
+ public function __construct(
+ \Traversable $requirementValidators,
+ ) {
+ if (iterator_count($requirementValidators) === 0) {
+ throw new \Exception('Requirement validators not found');
+ }
+ $this->requirementValidators = iterator_to_array($requirementValidators);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateRequirementsOrFail(): void
+ {
+ foreach ($this->requirementValidators as $requirementValidator) {
+ $this->info('Validating platform requirement with ' . $requirementValidator::class);
+ $requirementValidator->validateRequirementOrFail();
+ }
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementException.php b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementException.php
new file mode 100644
index 00000000000..f9517d8216a
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementException.php
@@ -0,0 +1,49 @@
+ $dbRequirementValidators
+ *
+ * @throws \Exception
+ */
+ public function __construct(
+ DatabaseConnection $db,
+ \Traversable $dbRequirementValidators,
+ ) {
+ $this->db = $db;
+
+ if (iterator_count($dbRequirementValidators) === 0) {
+ throw new \Exception('Database requirement validators not found');
+ }
+ $this->dbRequirementValidators = iterator_to_array($dbRequirementValidators);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DatabaseRequirementException
+ */
+ public function validateRequirementOrFail(): void
+ {
+ $this->initDatabaseVersionInformation();
+
+ foreach ($this->dbRequirementValidators as $dbRequirementValidator) {
+ if ($dbRequirementValidator->isValidFor($this->versionComment)) {
+ $this->info(
+ 'Validating requirement by ' . $dbRequirementValidator::class,
+ [
+ 'current_version' => $this->version,
+ ],
+ );
+ $dbRequirementValidator->validateRequirementOrFail($this->version);
+ $this->info('Requirement validated by ' . $dbRequirementValidator::class);
+ }
+ }
+ }
+
+ /**
+ * Get database version information
+ *
+ * @throws DatabaseRequirementException
+ */
+ private function initDatabaseVersionInformation(): void
+ {
+ $this->info('Getting database version information');
+
+ try {
+ $statement = $this->db->query("SHOW VARIABLES WHERE Variable_name IN ('version', 'version_comment')");
+ while ($statement !== false && is_array($row = $statement->fetch(\PDO::FETCH_ASSOC))) {
+ if ($row['Variable_name'] === "version") {
+ $this->info('Retrieved DBMS version: ' . $row['Value']);
+ $this->version = $row['Value'];
+ } elseif ($row['Variable_name'] === "version_comment") {
+ $this->info('Retrieved DBMS version comment: ' . $row['Value']);
+ $this->versionComment = $row['Value'];
+ }
+ }
+ } catch (\Throwable $e) {
+ $this->error(
+ 'Error when getting DBMS version from database',
+ [
+ 'message' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString(),
+ ],
+ );
+ throw DatabaseRequirementException::errorWhenGettingDatabaseVersion($e);
+ }
+
+ if (empty($this->version) || empty($this->versionComment)) {
+ $this->info('Cannot retrieve the database version information');
+ throw DatabaseRequirementException::cannotRetrieveVersionInformation();
+ }
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidatorInterface.php b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidatorInterface.php
new file mode 100644
index 00000000000..ba44860931b
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/DatabaseRequirementValidatorInterface.php
@@ -0,0 +1,43 @@
+info(
+ 'Checking if version comment contains MariaDB string',
+ [
+ 'version_comment' => $versionComment,
+ ],
+ );
+
+ return strpos($versionComment, "MariaDB") !== false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MariaDbRequirementException
+ */
+ public function validateRequirementOrFail(string $version): void
+ {
+ $currentMariaDBMajorVersion = VersionHelper::regularizeDepthVersion($version, 1);
+
+ $this->info(
+ 'Comparing current MariaDB version ' . $currentMariaDBMajorVersion
+ . ' to minimal required version ' . $this->requiredMariaDbMinVersion
+ );
+
+ if (
+ VersionHelper::compare($currentMariaDBMajorVersion, $this->requiredMariaDbMinVersion, VersionHelper::LT)
+ ) {
+ $this->error('MariaDB requirement is not validated');
+
+ throw MariaDbRequirementException::badMariaDbVersion(
+ $this->requiredMariaDbMinVersion,
+ $currentMariaDBMajorVersion,
+ );
+ }
+ }
+}
diff --git a/src/Core/Platform/Infrastructure/Validator/RequirementValidators/PhpRequirementException.php b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/PhpRequirementException.php
new file mode 100644
index 00000000000..8086d3d86bc
--- /dev/null
+++ b/src/Core/Platform/Infrastructure/Validator/RequirementValidators/PhpRequirementException.php
@@ -0,0 +1,56 @@
+validatePhpVersionOrFail();
+ $this->validatePhpExtensionsOrFail();
+ }
+
+ /**
+ * Check installed php version
+ *
+ * @throws PhpRequirementException
+ */
+ private function validatePhpVersionOrFail(): void
+ {
+ $currentPhpMajorVersion = VersionHelper::regularizeDepthVersion(PHP_VERSION, 1);
+
+ $this->info(
+ 'Comparing current PHP version ' . $currentPhpMajorVersion
+ . ' to required version ' . $this->requiredPhpVersion
+ );
+ if (! VersionHelper::compare($currentPhpMajorVersion, $this->requiredPhpVersion, VersionHelper::EQUAL)) {
+ throw PhpRequirementException::badPhpVersion($this->requiredPhpVersion, $currentPhpMajorVersion);
+ }
+ }
+
+ /**
+ * Check if required php extensions are loaded
+ *
+ * @throws PhpRequirementException
+ */
+ private function validatePhpExtensionsOrFail(): void
+ {
+ $this->info('Checking PHP extensions');
+ foreach (self::EXTENSION_REQUIREMENTS as $extensionName) {
+ $this->validatePhpExtensionOrFail($extensionName);
+ }
+ }
+
+ /**
+ * check if given php extension is loaded
+ *
+ * @param string $extensionName
+ *
+ * @throws PhpRequirementException
+ */
+ private function validatePhpExtensionOrFail(string $extensionName): void
+ {
+ $this->info('Checking PHP extension ' . $extensionName);
+ if (! extension_loaded($extensionName)) {
+ $this->error('PHP extension ' . $extensionName . ' is not loaded');
+ throw PhpRequirementException::phpExtensionNotLoaded($extensionName);
+ }
+ }
+}
diff --git a/tests/api/Context/PlatformUpdateContext.php b/tests/api/Context/PlatformUpdateContext.php
new file mode 100644
index 00000000000..842bd5c0379
--- /dev/null
+++ b/tests/api/Context/PlatformUpdateContext.php
@@ -0,0 +1,48 @@
+getContainer()->execute(
+ 'mkdir -p /usr/share/centreon/www/install/php',
+ 'web'
+ );
+ $this->getContainer()->execute(
+ "sh -c 'echo \" /usr/share/centreon/www/install/php/Update-99.99.99.php'",
+ 'web'
+ );
+ $this->getContainer()->execute(
+ 'chown -R apache. /usr/share/centreon/www/install',
+ 'web'
+ );
+ }
+}
diff --git a/tests/api/behat.yml b/tests/api/behat.yml
index be4954bf1ae..201f97cd472 100644
--- a/tests/api/behat.yml
+++ b/tests/api/behat.yml
@@ -72,6 +72,10 @@ default:
paths: [ "%paths.base%/features/PlatformInformation.feature" ]
contexts:
- Centreon\Test\Api\Context\PlatformInformationContext
+ platform_update:
+ paths: [ "%paths.base%/features/PlatformUpdate.feature" ]
+ contexts:
+ - Centreon\Test\Api\Context\PlatformUpdateContext
host_groups:
paths: [ "%paths.base%/features/HostGroup.feature" ]
contexts:
diff --git a/tests/api/features/PlatformUpdate.feature b/tests/api/features/PlatformUpdate.feature
new file mode 100644
index 00000000000..624d875699d
--- /dev/null
+++ b/tests/api/features/PlatformUpdate.feature
@@ -0,0 +1,41 @@
+Feature:
+ In order to maintain easily centreon platform
+ As a user
+ I want to update centreon web using api
+
+ Background:
+ Given a running instance of Centreon Web API
+ And the endpoints are described in Centreon Web API documentation
+
+ Scenario: Update platform information
+ Given I am logged in
+
+ When an update is available
+ And I send a PATCH request to '/api/latest/platform/updates' with body:
+ """
+ {
+ "components": [
+ {
+ "name": "centreon-web"
+ }
+ ]
+ }
+ """
+ Then the response code should be "204"
+
+ When I send a GET request to '/api/latest/platform/versions'
+ Then the response code should be "200"
+ And the JSON node "web.version" should be equal to the string "99.99.99"
+
+ When I send a PATCH request to '/api/latest/platform/updates' with body:
+ """
+ {
+ "components": [
+ {
+ "name": "centreon-web"
+ }
+ ]
+ }
+ """
+ Then the response code should be "404"
+ And the JSON node "message" should be equal to the string "Updates not found"
\ No newline at end of file
diff --git a/tests/php/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsTest.php b/tests/php/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsTest.php
new file mode 100644
index 00000000000..6f96a95531a
--- /dev/null
+++ b/tests/php/Core/Platform/Application/UseCase/UpdateVersions/UpdateVersionsTest.php
@@ -0,0 +1,157 @@
+requirementValidators = $this->createMock(RequirementValidatorsInterface::class);
+ $this->updateLockerRepository = $this->createMock(UpdateLockerRepositoryInterface::class);
+ $this->readVersionRepository = $this->createMock(ReadVersionRepositoryInterface::class);
+ $this->readUpdateRepository = $this->createMock(ReadUpdateRepositoryInterface::class);
+ $this->writeUpdateRepository = $this->createMock(WriteUpdateRepositoryInterface::class);
+ $this->presenter = $this->createMock(UpdateVersionsPresenterInterface::class);
+});
+
+it('should stop update process when an other update is already started', function () {
+ $updateVersions = new UpdateVersions(
+ $this->requirementValidators,
+ $this->updateLockerRepository,
+ $this->readVersionRepository,
+ $this->readUpdateRepository,
+ $this->writeUpdateRepository,
+ );
+
+ $this->updateLockerRepository
+ ->expects($this->once())
+ ->method('lock')
+ ->willReturn(false);
+
+ $this->presenter
+ ->expects($this->once())
+ ->method('setResponseStatus')
+ ->with(new ErrorResponse('Update already in progress'));
+
+ $updateVersions($this->presenter);
+});
+
+it('should present an error response if a requirement is not validated', function () {
+ $updateVersions = new UpdateVersions(
+ $this->requirementValidators,
+ $this->updateLockerRepository,
+ $this->readVersionRepository,
+ $this->readUpdateRepository,
+ $this->writeUpdateRepository,
+ );
+
+ $this->requirementValidators
+ ->expects($this->once())
+ ->method('validateRequirementsOrFail')
+ ->willThrowException(new RequirementException('Requirement is not validated'));
+
+ $this->presenter
+ ->expects($this->once())
+ ->method('setResponseStatus')
+ ->with(new ErrorResponse('Requirement is not validated'));
+
+ $updateVersions($this->presenter);
+});
+
+it('should present an error response if current centreon version is not found', function () {
+ $updateVersions = new UpdateVersions(
+ $this->requirementValidators,
+ $this->updateLockerRepository,
+ $this->readVersionRepository,
+ $this->readUpdateRepository,
+ $this->writeUpdateRepository,
+ );
+
+ $this->updateLockerRepository
+ ->expects($this->once())
+ ->method('lock')
+ ->willReturn(true);
+
+ $this->readVersionRepository
+ ->expects($this->once())
+ ->method('findCurrentVersion')
+ ->willReturn(null);
+
+ $this->presenter
+ ->expects($this->once())
+ ->method('setResponseStatus')
+ ->with(new ErrorResponse('Cannot retrieve the current version'));
+
+ $updateVersions($this->presenter);
+});
+
+it('should run found updates', function () {
+ $updateVersions = new UpdateVersions(
+ $this->requirementValidators,
+ $this->updateLockerRepository,
+ $this->readVersionRepository,
+ $this->readUpdateRepository,
+ $this->writeUpdateRepository,
+ );
+
+ $this->updateLockerRepository
+ ->expects($this->once())
+ ->method('lock')
+ ->willReturn(true);
+
+ $this->readVersionRepository
+ ->expects($this->exactly(2))
+ ->method('findCurrentVersion')
+ ->will($this->onConsecutiveCalls('22.04.0', '22.10.1'));
+
+ $this->readUpdateRepository
+ ->expects($this->once())
+ ->method('findOrderedAvailableUpdates')
+ ->with('22.04.0')
+ ->willReturn(['22.10.0-beta.1', '22.10.0', '22.10.1']);
+
+ $this->writeUpdateRepository
+ ->expects($this->exactly(3))
+ ->method('runUpdate')
+ ->withConsecutive(
+ [$this->equalTo('22.10.0-beta.1')],
+ [$this->equalTo('22.10.0')],
+ [$this->equalTo('22.10.1')],
+ );
+
+ $this->presenter
+ ->expects($this->once())
+ ->method('setResponseStatus')
+ ->with(new NoContentResponse());
+
+ $updateVersions($this->presenter);
+});
diff --git a/tests/php/Core/Platform/Infrastructure/Repository/FsReadUpdateRepositoryTest.php b/tests/php/Core/Platform/Infrastructure/Repository/FsReadUpdateRepositoryTest.php
new file mode 100644
index 00000000000..16682e3ea3c
--- /dev/null
+++ b/tests/php/Core/Platform/Infrastructure/Repository/FsReadUpdateRepositoryTest.php
@@ -0,0 +1,89 @@
+filesystem = $this->createMock(Filesystem::class);
+ $this->finder = $this->createMock(Finder::class);
+});
+
+it('should return an error when install directory does not exist', function () {
+ $repository = new FsReadUpdateRepository(sys_get_temp_dir(), $this->filesystem, $this->finder);
+
+ $this->filesystem
+ ->expects($this->once())
+ ->method('exists')
+ ->willReturn(false);
+
+ $availableUpdates = $repository->findOrderedAvailableUpdates('22.04.0');
+})->throws(
+ UpdateNotFoundException::class,
+ UpdateNotFoundException::updatesNotFound()->getMessage(),
+);
+
+it('should order found updates', function () {
+ $repository = new FsReadUpdateRepository(sys_get_temp_dir(), $this->filesystem, $this->finder);
+
+ $this->filesystem
+ ->expects($this->once())
+ ->method('exists')
+ ->willReturn(true);
+
+ $this->finder
+ ->expects($this->once())
+ ->method('files')
+ ->willReturn($this->finder);
+
+ $this->finder
+ ->expects($this->once())
+ ->method('in')
+ ->willReturn($this->finder);
+
+ $this->finder
+ ->expects($this->once())
+ ->method('name')
+ ->willReturn(
+ [
+ new \SplFileInfo('Update-21.10.0.php'),
+ new \SplFileInfo('Update-22.04.0.php'),
+ new \SplFileInfo('Update-22.10.11.php'),
+ new \SplFileInfo('Update-22.10.1.php'),
+ new \SplFileInfo('Update-22.10.0-beta.3.php'),
+ new \SplFileInfo('Update-22.10.0-alpha.1.php'),
+ ]
+ );
+
+ $availableUpdates = $repository->findOrderedAvailableUpdates('22.04.0');
+ expect($availableUpdates)->toEqual([
+ '22.10.0-alpha.1',
+ '22.10.0-beta.3',
+ '22.10.1',
+ '22.10.11'
+ ]);
+});
diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php
index 2ca3e800b03..d1d41179b9f 100644
--- a/tests/php/bootstrap.php
+++ b/tests/php/bootstrap.php
@@ -24,8 +24,7 @@
}
$mockedPreRequisiteConstants = [
- '_CENTREON_PHP_MIN_VERSION_' => '8.0',
- '_CENTREON_PHP_MAX_VERSION_' => '8.0',
+ '_CENTREON_PHP_VERSION_' => '8.0',
'_CENTREON_MARIA_DB_MIN_VERSION_' => '10.5',
];
foreach ($mockedPreRequisiteConstants as $mockedPreRequisiteConstant => $value) {
diff --git a/www/install/step_upgrade/process/process_step4.php b/www/install/step_upgrade/process/process_step4.php
index 308c61a21af..95b771fc5de 100644
--- a/www/install/step_upgrade/process/process_step4.php
+++ b/www/install/step_upgrade/process/process_step4.php
@@ -1,128 +1,62 @@
.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Linking this program statically or dynamically with other modules is making a
- * combined work based on this program. Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this program give Centreon
- * permission to link this program with independent modules to produce an executable,
- * regardless of the license terms of these independent modules, and to copy and
- * distribute the resulting executable under terms of Centreon choice, provided that
- * Centreon also meet, for each linked independent module, the terms and conditions
- * of the license of that module. An independent module is a module which is not
- * derived from this program. If you modify this program, you may extend this
- * exception to your version of the program, but you are not obliged to do so. If you
- * do not wish to do so, delete this exception statement from your version.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*
* For more information : contact@centreon.com
*
*/
session_start();
-require_once realpath(dirname(__FILE__) . "/../../../../config/centreon.config.php");
-require_once _CENTREON_PATH_ . '/www/class/centreonDB.class.php';
-require_once '../../steps/functions.php';
+require_once __DIR__ . '/../../../../bootstrap.php';
+require_once __DIR__ . '/../../../class/centreonDB.class.php';
+require_once __DIR__ . '/../../steps/functions.php';
+
+use Core\Platform\Application\Repository\UpdateLockerRepositoryInterface;
+use Core\Platform\Application\Repository\ReadUpdateRepositoryInterface;
+use Core\Platform\Application\Repository\WriteUpdateRepositoryInterface;
+use Core\Platform\Application\UseCase\UpdateVersions\UpdateVersionsException;
$current = $_POST['current'];
$next = $_POST['next'];
$status = 0;
-/**
- * Variables for upgrade scripts
- */
-try {
- $pearDB = new CentreonDB('centreon', 3);
- $pearDBO = new CentreonDB('centstorage', 3);
-} catch (Exception $e) {
- exitUpgradeProcess(1, $current, $next, $e->getMessage());
-}
+$kernel = \App\Kernel::createForWeb();
-/**
- * Upgrade storage sql
- */
-$storageSql = '../../sql/centstorage/Update-CSTG-' . $next . '.sql';
-if (is_file($storageSql)) {
- $result = splitQueries($storageSql, ';', $pearDBO, '../../tmp/Update-CSTG-' . $next);
- if ("0" != $result) {
- exitUpgradeProcess(1, $current, $next, $result);
- }
-}
+$updateLockerRepository = $kernel->getContainer()->get(UpdateLockerRepositoryInterface::class);
+$updateWriteRepository = $kernel->getContainer()->get(WriteUpdateRepositoryInterface::class);
-/**
- * Pre upgrade PHP
- */
-$prePhp = '../../php/Update-' . $next . '.php';
-if (is_file($prePhp)) {
- try {
- include_once $prePhp;
- } catch (Exception $e) {
- exitUpgradeProcess(1, $current, $next, $e->getMessage());
+try {
+ if (! $updateLockerRepository->lock()) {
+ throw UpdateVersionsException::updateAlreadyInProgress();
}
-}
-/**
- * Upgrade configuration sql
- */
-$confSql = '../../sql/centreon/Update-DB-' . $next . '.sql';
-if (is_file($confSql)) {
- $result = splitQueries($confSql, ';', $pearDB, '../../tmp/Update-DB-' . $next);
- if ("0" != $result) {
- exitUpgradeProcess(1, $current, $next, $result);
- }
-}
+ $updateWriteRepository->runUpdate($next);
-/**
- * Post upgrade PHP
- */
-$postPhp = '../../php/Update-' . $next . '.post.php';
-if (is_file($postPhp)) {
- try {
- include_once $postPhp;
- } catch (Exception $e) {
- exitUpgradeProcess(1, $current, $next, $e->getMessage());
- }
+ $updateLockerRepository->unlock();
+} catch (\Throwable $e) {
+ exitUpgradeProcess(1, $current, $next, $e->getMessage());
}
-/**
- * Update version in database.
- */
-$res = $pearDB->prepare("UPDATE `informations` SET `value` = ? WHERE `key` = 'version'");
-$res->execute(array($next));
$current = $next;
-/*
-** To find the next version that we should update to, we will look in
-** the www/install/php directory where all PHP update scripts are
-** stored. We will extract the target version from the filename and find
-** the closest version to the current version.
-*/
-$next = '';
-if ($handle = opendir('../../php')) {
- while (false !== ($file = readdir($handle))) {
- if (preg_match('/Update-([a-zA-Z0-9\-\.]+)\.php/', $file, $matches)) {
- if ((version_compare($current, $matches[1]) < 0) &&
- (empty($next) || (version_compare($matches[1], $next) < 0))) {
- $next = $matches[1];
- }
- }
- }
- closedir($handle);
-}
+$updateReadRepository = $kernel->getContainer()->get(ReadUpdateRepositoryInterface::class);
+$availableUpdates = $updateReadRepository->findOrderedAvailableUpdates($current);
+$next = empty($availableUpdates) ? '' : array_shift($availableUpdates);
+
$_SESSION['CURRENT_VERSION'] = $current;
$okMsg = "OK ";
+
exitUpgradeProcess($status, $current, $next, $okMsg);
diff --git a/www/install/step_upgrade/process/process_step5.php b/www/install/step_upgrade/process/process_step5.php
index df5d79e2174..c4a723a14f2 100644
--- a/www/install/step_upgrade/process/process_step5.php
+++ b/www/install/step_upgrade/process/process_step5.php
@@ -37,44 +37,15 @@
require_once __DIR__ . '/../../../../bootstrap.php';
require_once '../../steps/functions.php';
-function recurseRmdir($dir)
-{
- $files = array_diff(scandir($dir), array('.', '..'));
- foreach ($files as $file) {
- (is_dir("$dir/$file")) ? recurseRmdir("$dir/$file") : unlink("$dir/$file");
- }
- return rmdir($dir);
-}
-
-function recurseCopy($source, $dest)
-{
- if (is_link($source)) {
- return symlink(readlink($source), $dest);
- }
+use Core\Platform\Application\Repository\WriteUpdateRepositoryInterface;
+use Core\Platform\Application\UseCase\UpdateVersions\UpdateVersionsException;
- if (is_file($source)) {
- return copy($source, $dest);
- }
-
- if (!is_dir($dest)) {
- mkdir($dest);
- }
+$kernel = \App\Kernel::createForWeb();
- $dir = dir($source);
- while (false !== $entry = $dir->read()) {
- if ($entry == '.' || $entry == '..') {
- continue;
- }
-
- recurseCopy("$source/$entry", "$dest/$entry");
- }
-
- $dir->close();
- return true;
-}
+$updateWriteRepository = $kernel->getContainer()->get(WriteUpdateRepositoryInterface::class);
$parameters = filter_input_array(INPUT_POST);
-$current = filter_var($_POST['current'] ?? "step 5", FILTER_SANITIZE_STRING);
+$current = filter_var($_POST['current'] ?? "step 5", FILTER_SANITIZE_FULL_SPECIAL_CHARS);
if ($parameters) {
if ((int)$parameters["send_statistics"] === 1) {
@@ -88,16 +59,19 @@ function recurseCopy($source, $dest)
$db->query($query);
}
-$name = 'install-' . $_SESSION['CURRENT_VERSION'] . '-' . date('Ymd_His');
-$completeName = _CENTREON_VARLIB_ . '/installs/' . $name;
-$sourceInstallDir = str_replace('step_upgrade', '', realpath(dirname(__FILE__) . '/../'));
-
try {
- if (recurseCopy($sourceInstallDir, $completeName)) {
- recurseRmdir($sourceInstallDir);
+ if (!isset($_SESSION['CURRENT_VERSION']) || ! preg_match('/^\d+\.\d+\.\d+/', $_SESSION['CURRENT_VERSION'])) {
+ throw new \Exception('Cannot get current version');
}
-} catch (Exception $e) {
- exitUpgradeProcess(1, $current, '', $e->getMessage());
+
+ $updateWriteRepository->runPostUpdate($_SESSION['CURRENT_VERSION']);
+} catch (\Throwable $e) {
+ exitUpgradeProcess(
+ 1,
+ $current,
+ '',
+ UpdateVersionsException::errorWhenApplyingPostUpdate($e)->getMessage()
+ );
}
session_destroy();
From 0f71be5910ccd565ad87bf3a6c934317a55e16b6 Mon Sep 17 00:00:00 2001
From: hyahiaoui-ext <97593234+hyahiaoui-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 15:49:54 +0100
Subject: [PATCH 14/18] Clean(platform): Clean appKey method and usage 22.04.x
(#11452)
* Clean(platform): Clean appKey method and usage (#11336)
* removing appKey from information table in baseConf and 22.10 update script
* removing appKey from NotifyMasterService.php
* removing appKey from CentreonRemoteServer.php
* applying suggested changes
* Applying suggested changes
Co-authored-by: Kevin Duret
* adding 22.04.2 update script file with changes
* revert 22.04 beta 1 script to its original
Co-authored-by: Kevin Duret
---
.../Webservice/CentreonRemoteServer.php | 13 +----
.../Domain/Service/NotifyMasterService.php | 9 ----
www/install/createTables.sql | 1 -
www/install/php/Update-22.04.2.php | 51 +++++++++++++++++++
www/install/steps/process/insertBaseConf.php | 2 -
5 files changed, 53 insertions(+), 23 deletions(-)
create mode 100644 www/install/php/Update-22.04.2.php
diff --git a/src/CentreonRemote/Application/Webservice/CentreonRemoteServer.php b/src/CentreonRemote/Application/Webservice/CentreonRemoteServer.php
index 40768c67cfe..8bfd90de78f 100644
--- a/src/CentreonRemote/Application/Webservice/CentreonRemoteServer.php
+++ b/src/CentreonRemote/Application/Webservice/CentreonRemoteServer.php
@@ -109,14 +109,6 @@ public function postAddToWaitList(): string
throw new \RestBadRequestException('Can not access your address.');
}
- if (
- !isset($_POST['app_key'])
- || !$_POST['app_key']
- || empty($appKey = filter_var($_POST['app_key'], FILTER_SANITIZE_STRING))
- ) {
- throw new \RestBadRequestException('Please send \'app_key\' in the request.');
- }
-
if (
!isset($_POST['version'])
|| !$_POST['version']
@@ -147,15 +139,14 @@ public function postAddToWaitList(): string
}
$createdAt = date('Y-m-d H:i:s');
- $insertQuery = "INSERT INTO `remote_servers` (`ip`, `app_key`, `version`, `is_connected`,
+ $insertQuery = "INSERT INTO `remote_servers` (`ip`, `version`, `is_connected`,
`created_at`, `http_method`, `http_port`, `no_check_certificate`)
- VALUES (:ip, :app_key, :version, 0, '{$createdAt}',
+ VALUES (:ip, :version, 0, '{$createdAt}',
:http_method, :http_port, :no_check_certificate
)";
$insert = $this->pearDB->prepare($insertQuery);
$insert->bindValue(':ip', $ip, \PDO::PARAM_STR);
- $insert->bindValue(':app_key', $appKey, \PDO::PARAM_STR);
$insert->bindValue(':version', $version, \PDO::PARAM_STR);
$insert->bindValue(':http_method', $httpScheme, \PDO::PARAM_STR);
$insert->bindValue(':http_port', $httpPort, \PDO::PARAM_INT);
diff --git a/src/CentreonRemote/Domain/Service/NotifyMasterService.php b/src/CentreonRemote/Domain/Service/NotifyMasterService.php
index d5b0295f933..9af5d665ddb 100644
--- a/src/CentreonRemote/Domain/Service/NotifyMasterService.php
+++ b/src/CentreonRemote/Domain/Service/NotifyMasterService.php
@@ -93,19 +93,10 @@ public function pingMaster($ip, $data, $noCheckCertificate = false, $noProxy = f
$url = "{$ip}/centreon/api/external.php?object=centreon_remote_server&action=addToWaitList";
$repository = $this->dbManager->getRepository(InformationsRepository::class);
- $applicationKey = $repository->getOneByKey('appKey');
$version = $repository->getOneByKey('version');
- if (empty($applicationKey)) {
- return [
- 'status' => self::FAIL,
- 'details' => self::NO_APP_KEY
- ];
- }
-
try {
$curlData = [
- 'app_key' => $applicationKey->getValue(),
'version' => $version->getValue(),
'http_method' => $data['remoteHttpMethod'] ?? 'http',
'http_port' => $data['remoteHttpPort'] ?? '',
diff --git a/www/install/createTables.sql b/www/install/createTables.sql
index aa1a86c661d..c72f2449beb 100644
--- a/www/install/createTables.sql
+++ b/www/install/createTables.sql
@@ -2321,7 +2321,6 @@ CREATE TABLE IF NOT EXISTS contact_feature (
CREATE TABLE IF NOT EXISTS `remote_servers` (
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`ip` VARCHAR(255) NOT NULL,
- `app_key` VARCHAR(40) NOT NULL,
`version` VARCHAR(16) NOT NULL,
`is_connected` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` TIMESTAMP NOT NULL,
diff --git a/www/install/php/Update-22.04.2.php b/www/install/php/Update-22.04.2.php
new file mode 100644
index 00000000000..18f7e537d45
--- /dev/null
+++ b/www/install/php/Update-22.04.2.php
@@ -0,0 +1,51 @@
+beginTransaction();
+
+ $errorMessage = "Unable to delete 'appKey' information from database";
+ $pearDB->query("DELETE FROM `informations` WHERE `key` = 'appKey'");
+
+ $pearDB->commit();
+} catch (\Exception $e) {
+ if ($pearDB->inTransaction()) {
+ $pearDB->rollBack();
+ }
+
+ $centreonLog->insertLog(
+ 4,
+ $versionOfTheUpgrade . $errorMessage .
+ " - Code : " . (int)$e->getCode() .
+ " - Error : " . $e->getMessage() .
+ " - Trace : " . $e->getTraceAsString()
+ );
+
+ throw new \Exception($versionOfTheUpgrade . $errorMessage, (int) $e->getCode(), $e);
+}
diff --git a/www/install/steps/process/insertBaseConf.php b/www/install/steps/process/insertBaseConf.php
index 5a2fe96e73a..95f3e2bab70 100644
--- a/www/install/steps/process/insertBaseConf.php
+++ b/www/install/steps/process/insertBaseConf.php
@@ -138,9 +138,7 @@
$link->exec("INSERT INTO `options` (`key`, `value`) VALUES ('gmt','" . $timezoneId . "')");
# Generate random key for this instance and set it to be not central and not remote
-$uniqueKey = md5(uniqid(rand(), true));
$informationsTableInsert = "INSERT INTO `informations` (`key`,`value`) VALUES
- ('appKey', '{$uniqueKey}'),
('isRemote', 'no'),
('isCentral', 'yes')";
From 726d4d28badc088cc8283c290c8b3730332736bb Mon Sep 17 00:00:00 2001
From: hyahiaoui-ext <97593234+hyahiaoui-ext@users.noreply.github.com>
Date: Fri, 29 Jul 2022 16:35:36 +0100
Subject: [PATCH 15/18] enh(platform): Use API to select metrics in virtual
metrics configuration form 22.04.x (#11461)
* changing select with select2 of metrics
* fix alignement
* remove unecessary files and replace selec by select2 in formComponentTemplate
* fix select id name for acceptance tests
* update composer for acceptance tests
* fix acceptance test 2
* add allow clear to metrics select2
* applying suggested changes
* final changes for merging
* remove unecessary select tag
---
composer.lock | 12 +-
.../bootstrap/VirtualMetricHandleContext.php | 3 +-
.../formComponentTemplate.ihtml | 3 +
.../formComponentTemplate.php | 22 ++-
.../graphs/common/makeJS_formMetricsList.php | 177 ------------------
.../graphs/common/makeXML_ListMetrics.php | 173 -----------------
.../virtualMetrics/formVirtualMetrics.ihtml | 5 +-
.../virtualMetrics/formVirtualMetrics.php | 24 ++-
8 files changed, 48 insertions(+), 371 deletions(-)
delete mode 100644 www/include/views/graphs/common/makeJS_formMetricsList.php
delete mode 100644 www/include/views/graphs/common/makeXML_ListMetrics.php
diff --git a/composer.lock b/composer.lock
index 6dc53f71f39..bf11612a345 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1c4ccf2b3d1f768dfd30298637120a27",
+ "content-hash": "668e34fd2ddb66b073d8e525d65c166a",
"packages": [
{
"name": "beberlei/assert",
@@ -6628,12 +6628,12 @@
"source": {
"type": "git",
"url": "https://github.com/centreon/centreon-test-lib.git",
- "reference": "2aed30ebf46d7b76478166fdf122112a1c3722c6"
+ "reference": "1f51490fe248f71e8c464c4ebfb5d0f04af7a129"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/centreon/centreon-test-lib/zipball/2aed30ebf46d7b76478166fdf122112a1c3722c6",
- "reference": "2aed30ebf46d7b76478166fdf122112a1c3722c6",
+ "url": "https://api.github.com/repos/centreon/centreon-test-lib/zipball/1f51490fe248f71e8c464c4ebfb5d0f04af7a129",
+ "reference": "1f51490fe248f71e8c464c4ebfb5d0f04af7a129",
"shasum": ""
},
"require": {
@@ -6678,7 +6678,7 @@
"issues": "https://github.com/centreon/centreon-test-lib/issues",
"source": "https://github.com/centreon/centreon-test-lib/tree/master"
},
- "time": "2022-04-27T09:10:57+00:00"
+ "time": "2022-07-29T09:34:33+00:00"
},
{
"name": "facade/ignition-contracts",
@@ -10964,5 +10964,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/features/bootstrap/VirtualMetricHandleContext.php b/features/bootstrap/VirtualMetricHandleContext.php
index 47c44733822..16130030d81 100644
--- a/features/bootstrap/VirtualMetricHandleContext.php
+++ b/features/bootstrap/VirtualMetricHandleContext.php
@@ -24,7 +24,8 @@ public function iAddAVirtualMetric()
$this->page = new MetricsConfigurationPage($this);
$this->page->setProperties(array(
'name' => $this->vmName,
- 'linked-host_services' => $this->host . ' - ' . $this->hostService
+ 'linked-host_services' => $this->host . ' - ' . $this->hostService,
+ 'known_metrics' => $this->functionRPN,
));
$this->page->setProperties(array('function' => $this->functionRPN));
$this->page->save();
diff --git a/www/include/views/componentTemplates/formComponentTemplate.ihtml b/www/include/views/componentTemplates/formComponentTemplate.ihtml
index 8afbb317413..ede927f9029 100644
--- a/www/include/views/componentTemplates/formComponentTemplate.ihtml
+++ b/www/include/views/componentTemplates/formComponentTemplate.ihtml
@@ -35,6 +35,9 @@
+
+
+
{/if}
diff --git a/www/include/views/componentTemplates/formComponentTemplate.php b/www/include/views/componentTemplates/formComponentTemplate.php
index faaec375c5b..4b27228c4e3 100644
--- a/www/include/views/componentTemplates/formComponentTemplate.php
+++ b/www/include/views/componentTemplates/formComponentTemplate.php
@@ -359,7 +359,7 @@ function insertValueQuery() {
var e_input = document.Form.ds_name;
var e_select = document.getElementById('sl_list_metrics');
var sd_o = e_select.selectedIndex;
- if (sd_o != 0) {
+ if (sd_o != -1) {
var chaineAj = '';
chaineAj = e_select.options[sd_o].text;
chaineAj = chaineAj.replace(/\s(\[[CV]DEF\]|)\s*$/, "");
@@ -431,7 +431,6 @@ function popup_color_picker(t,name)
}
$vdef = 0; /* don't list VDEF in metrics list */
-include_once('./include/views/graphs/common/makeJS_formMetricsList.php');
if ($o === MODIFY_COMPONENT_TEMPLATE || $o === WATCH_COMPONENT_TEMPLATE) {
$host_service_id = filter_var(
$_POST['host_service_id'] ?? ($compo["host_id"] . '-' . $compo['service_id']),
@@ -446,9 +445,20 @@ function popup_color_picker(t,name)
?>
diff --git a/www/include/views/graphs/common/makeJS_formMetricsList.php b/www/include/views/graphs/common/makeJS_formMetricsList.php
deleted file mode 100644
index b817ce94a11..00000000000
--- a/www/include/views/graphs/common/makeJS_formMetricsList.php
+++ /dev/null
@@ -1,177 +0,0 @@
-.
- *
- * Linking this program statically or dynamically with other modules is making a
- * combined work based on this program. Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this program give Centreon
- * permission to link this program with independent modules to produce an executable,
- * regardless of the license terms of these independent modules, and to copy and
- * distribute the resulting executable under terms of Centreon choice, provided that
- * Centreon also meet, for each linked independent module, the terms and conditions
- * of the license of that module. An independent module is a module which is not
- * derived from this program. If you modify this program, you may extend this
- * exception to your version of the program, but you are not obliged to do so. If you
- * do not wish to do so, delete this exception statement from your version.
- *
- * For more information : contact@centreon.com
- *
- * SVN : $URL$
- * SVN : $Id$
- *
- */
-
- /*
- * Lang file
- */
- $locale = $oreon->user->get_lang();
- putenv("LANG=$locale");
- setlocale(LC_ALL, $locale);
- bindtextdomain("messages", _CENTREON_PATH_ . "www/locale/");
- bind_textdomain_codeset("messages", "UTF-8");
- textdomain("messages");
-?>
diff --git a/www/include/views/graphs/common/makeXML_ListMetrics.php b/www/include/views/graphs/common/makeXML_ListMetrics.php
deleted file mode 100644
index 5d7afe858b1..00000000000
--- a/www/include/views/graphs/common/makeXML_ListMetrics.php
+++ /dev/null
@@ -1,173 +0,0 @@
-.
- *
- * Linking this program statically or dynamically with other modules is making a
- * combined work based on this program. Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this program give Centreon
- * permission to link this program with independent modules to produce an executable,
- * regardless of the license terms of these independent modules, and to copy and
- * distribute the resulting executable under terms of Centreon choice, provided that
- * Centreon also meet, for each linked independent module, the terms and conditions
- * of the license of that module. An independent module is a module which is not
- * derived from this program. If you modify this program, you may extend this
- * exception to your version of the program, but you are not obliged to do so. If you
- * do not wish to do so, delete this exception statement from your version.
- *
- * For more information : contact@centreon.com
- *
- * SVN : $URL$
- * SVN : $Id$
- *
- */
-
- header('Content-Type: text/xml');
- header('Cache-Control: no-cache');
-
- require_once realpath(dirname(__FILE__) . "/../../../../../config/centreon.config.php");
- require_once _CENTREON_PATH_."/www/class/centreonDB.class.php";
- require_once _CENTREON_PATH_."/www/class/centreonXML.class.php";
-
-function compare($a, $b)
-{
- if ($a["metric_name"] == $b["metric_name"]) {
- return 0;
- }
- return ( $a["metric_name"] < $b["metric_name"] ) ? -1 : 1;
-}
-
- $pearDB = new CentreonDB();
- $pearDBO = new CentreonDB("centstorage");
-
- /*
- * Get session
- */
- require_once(_CENTREON_PATH_ . "www/class/centreonSession.class.php");
- require_once(_CENTREON_PATH_ . "www/class/centreon.class.php");
-if (!isset($_SESSION['centreon'])) {
- CentreonSession::start();
-}
-
-if (isset($_SESSION['centreon'])) {
- $oreon = $_SESSION['centreon'];
-} else {
- exit;
-}
-
- /*
- * Get language
- */
- $locale = $oreon->user->get_lang();
- putenv("LANG=$locale");
- setlocale(LC_ALL, $locale);
- bindtextdomain("messages", _CENTREON_PATH_ . "www/locale/");
-;
- bind_textdomain_codeset("messages", "UTF-8");
- textdomain("messages");
-
- #
- # Existing Real Metric List comes from DBO -> Store in $rmetrics Array
- #
- $s_datas = array();
- $o_datas = array(""=> utf8_decode(_("List of known metrics")));
- $mx_l = strlen($o_datas[""]);
- $where = "";
- $def_type = array(0=>"CDEF",1=>"VDEF");
-
-if (isset($_GET['vdef']) && is_numeric($_GET['vdef']) && $_GET['vdef'] == 0) {
- $where = " AND def_type='".$_GET["vdef"]."'";
-}
-
-if (isset($_GET["host_id"]) && $_GET["service_id"]) {
- if (!is_numeric($_GET['host_id']) || !is_numeric($_GET['service_id'])) {
- $buffer = new CentreonXML();
- $buffer->writeElement('error', 'Bad id format');
- $buffer->output();
- exit;
- }
- $host_id = $_GET["host_id"];
- $service_id = $_GET["service_id"];
-
- $query = "SELECT id "
- . "FROM index_data "
- . "WHERE host_id = " . $pearDB->escape($host_id) . " "
- . "AND service_id = " . $pearDB->escape($service_id) . " ";
-
- $index_id = 0;
- $pq_sql = $pearDBO->query($query);
- if ($row = $pq_sql->fetchRow()) {
- $index_id = $row['id'];
- }
-
- $query = "SELECT metric_id, metric_name "
- . "FROM metrics "
- . "WHERE index_id = " . $index_id . " ";
- $pq_sql = $pearDBO->query($query);
- while ($fw_sql = $pq_sql->fetchRow()) {
- $sd_l = strlen($fw_sql["metric_name"]);
- $fw_sql["metric_name"] = $fw_sql["metric_name"] . " ";
- $s_datas[] = $fw_sql;
- if ($sd_l > $mx_l) {
- $mx_l = $sd_l;
- }
- }
- $pq_sql->closeCursor();
- $query = "SELECT vmetric_id, vmetric_name, def_type "
- . "FROM virtual_metrics "
- . "WHERE index_id = " . $index_id . " "
- . $where . " ";
- $pq_sql = $pearDB->query($query);
-
- while ($fw_sql = $pq_sql->fetchRow()) {
- $sd_l = strlen($fw_sql["vmetric_name"]." [CDEF]");
- $fw_sql["metric_name"] = $fw_sql["vmetric_name"]." [".$def_type[$fw_sql["def_type"]]."] ";
- $fw_sql["metric_id"] = "v".$fw_sql["vmetric_id"];
- $s_datas[] = $fw_sql;
- if ($sd_l > $mx_l) {
- $mx_l = $sd_l;
- }
- $pq_sql->closeCursor();
- }
-}
-
- usort($s_datas, "compare");
-
-foreach ($s_datas as $key => $om) {
- $o_datas[$om["metric_id"]] = $om["metric_name"];
-}
-
-for ($i = strlen($o_datas[""]); $i != $mx_l; $i++) {
- $o_datas[""] .= " ";
-}
-
- # The first element of the select is empty
- $buffer = new CentreonXML();
- $buffer->startElement("options_data");
- $buffer->writeElement("td_id", "td_list_metrics");
- $buffer->writeElement("select_id", "sl_list_metrics");
-
- # Now we fill out the select with templates id and names
-foreach ($o_datas as $o_id => $o_alias) {
- $buffer->startElement("option");
- $buffer->writeElement("o_id", $o_id);
- $buffer->writeElement("o_alias", $o_alias);
- $buffer->endElement();
-}
-
- $buffer->endElement();
- $buffer->output();
diff --git a/www/include/views/virtualMetrics/formVirtualMetrics.ihtml b/www/include/views/virtualMetrics/formVirtualMetrics.ihtml
index 86c044fee1a..98ea810739a 100644
--- a/www/include/views/virtualMetrics/formVirtualMetrics.ihtml
+++ b/www/include/views/virtualMetrics/formVirtualMetrics.ihtml
@@ -48,7 +48,10 @@
{$form.rpn_function.html}
{if $o == "a" || $o == "c"}
-
+
+
+
+
{/if}
diff --git a/www/include/views/virtualMetrics/formVirtualMetrics.php b/www/include/views/virtualMetrics/formVirtualMetrics.php
index a90eb4cf6d6..4cf98e972b9 100644
--- a/www/include/views/virtualMetrics/formVirtualMetrics.php
+++ b/www/include/views/virtualMetrics/formVirtualMetrics.php
@@ -236,7 +236,7 @@ function insertValueQuery() {
var e_txtarea = document.Form.rpn_function;
var e_select = document.getElementById('sl_list_metrics');
var sd_o = e_select.selectedIndex;
- if (sd_o != 0) {
+ if (sd_o != -1) {
var chaineAj = '';
chaineAj = e_select.options[sd_o].text;
//chaineAj = chaineAj.substring(0, chaineAj.length - 3);
@@ -329,7 +329,7 @@ function manageVDEF() {
$tpl->display("formVirtualMetrics.ihtml");
}
$vdef = 1; /* Display VDEF too */
-include_once("./include/views/graphs/common/makeJS_formMetricsList.php");
+
if ($o == METRIC_MODIFY || $o == METRIC_WATCH) {
isset($_POST["host_id"]) && $_POST["host_id"] != null
? $host_service_id = $_POST["host_id"]
@@ -340,11 +340,21 @@ function manageVDEF() {
: $host_service_id = 0;
}
?>
-
From 5dda7fc75f10d4790e8aa4035499a4272e55ac71 Mon Sep 17 00:00:00 2001
From: Elmahdi ABBASSI <108519266+emabassi-ext@users.noreply.github.com>
Date: Sun, 31 Jul 2022 18:49:38 +0100
Subject: [PATCH 16/18] [SNYK] Sanitize and bind ACL class queries (#11392)
(#11472)
* Sanitize and bind ACL class queries
Queries sanitized and bound using PDO statement
* fix spaces
spaces between (int) cast and variables
* update file delete spaces after comma
* change variables names due to a review
* Line exceeds 120 characters; contains 123 characters
---
www/class/centreonACL.class.php | 50 ++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 19 deletions(-)
diff --git a/www/class/centreonACL.class.php b/www/class/centreonACL.class.php
index e1e244c7ca3..699419dd29a 100644
--- a/www/class/centreonACL.class.php
+++ b/www/class/centreonACL.class.php
@@ -400,15 +400,17 @@ private function setTopology()
if ($DBRESULT->rowCount()) {
$topology = array();
$tmp_topo_page = array();
- while ($topo_group = $DBRESULT->fetchRow()) {
- $query2 = "SELECT topology_topology_id, acl_topology_relations.access_right "
+ $statement = $centreonDb
+ ->prepare("SELECT topology_topology_id, acl_topology_relations.access_right "
. "FROM acl_topology_relations, acl_topology "
. "WHERE acl_topology.acl_topo_activate = '1' "
. "AND acl_topology.acl_topo_id = acl_topology_relations.acl_topo_id "
- . "AND acl_topology_relations.acl_topo_id = '" . $topo_group["acl_topology_id"] . "' "
- . "AND acl_topology_relations.access_right != 0"; // do not get "access none"
- $DBRESULT2 = $centreonDb->query($query2);
- while ($topo_page = $DBRESULT2->fetchRow()) {
+ . "AND acl_topology_relations.acl_topo_id = :acl_topology_id "
+ . "AND acl_topology_relations.access_right != 0");
+ while ($topo_group = $DBRESULT->fetchRow()) {
+ $statement->bindValue(':acl_topology_id', (int) $topo_group["acl_topology_id"], \PDO::PARAM_INT);
+ $statement->execute();
+ while ($topo_page = $statement->fetchRow()) {
$topology[] = (int) $topo_page["topology_topology_id"];
if (!isset($tmp_topo_page[$topo_page['topology_topology_id']])) {
$tmp_topo_page[$topo_page["topology_topology_id"]] = $topo_page["access_right"];
@@ -423,7 +425,7 @@ private function setTopology()
}
}
}
- $DBRESULT2->closeCursor();
+ $statement->closeCursor();
}
$DBRESULT->closeCursor();
@@ -1691,22 +1693,28 @@ public function updateACL($data = null)
$request = "SELECT group_id FROM centreon_acl " .
"WHERE host_id = " . $data['duplicate_host'] . " AND service_id IS NULL";
$DBRESULT = \CentreonDBInstance::getMonInstance()->query($request);
+ $hostAclStatement = \CentreonDBInstance::getMonInstance()
+ ->prepare("INSERT INTO centreon_acl (host_id, service_id, group_id) "
+ . "VALUES (:data_id, NULL, :group_id)");
+ $serviceAclStatement = \CentreonDBInstance::getMonInstance()
+ ->prepare("INSERT INTO centreon_acl (host_id, service_id, group_id) "
+ . "VALUES (:data_id, :service_id, :group_id) "
+ . "ON DUPLICATE KEY UPDATE group_id = :group_id");
while ($row = $DBRESULT->fetchRow()) {
// Insert New Host
- $request1 = "INSERT INTO centreon_acl (host_id, service_id, group_id) "
- . "VALUES ('" . $data["id"] . "', NULL, " . $row['group_id'] . ")";
- \CentreonDBInstance::getMonInstance()->query($request1);
-
+ $hostAclStatement->bindValue(':data_id', (int) $data["id"], \PDO::PARAM_INT);
+ $hostAclStatement->bindValue(':group_id', (int) $row['group_id'], \PDO::PARAM_INT);
+ $hostAclStatement->execute();
// Insert services
$request = "SELECT service_id, group_id FROM centreon_acl "
. "WHERE host_id = " . $data['duplicate_host'] . " AND service_id IS NOT NULL";
$DBRESULT2 = \CentreonDBInstance::getMonInstance()->query($request);
while ($row2 = $DBRESULT2->fetch()) {
- $request2 = "INSERT INTO centreon_acl (host_id, service_id, group_id) "
- . "VALUES ('" . $data["id"] . "', "
- . "'" . $row2["service_id"] . "', " . $row2['group_id'] . ") "
- . "ON DUPLICATE KEY UPDATE group_id = " . $row2['group_id'];
- \CentreonDBInstance::getMonInstance()->query($request2);
+ $serviceAclStatement->bindValue(':data_id', (int) $data["id"], \PDO::PARAM_INT);
+ $serviceAclStatement
+ ->bindValue(':service_id', (int) $row2["service_id"], \PDO::PARAM_INT);
+ $serviceAclStatement->bindValue(':group_id', (int) $row2['group_id'], \PDO::PARAM_INT);
+ $serviceAclStatement->execute();
}
}
}
@@ -1730,10 +1738,14 @@ public function updateACL($data = null)
$request = "SELECT group_id FROM centreon_acl "
. "WHERE host_id = $host_id AND service_id = " . $data['duplicate_service'];
$DBRESULT = \CentreonDBInstance::getMonInstance()->query($request);
+ $statement = \CentreonDBInstance::getMonInstance()
+ ->prepare("INSERT INTO centreon_acl (host_id, service_id, group_id) "
+ . "VALUES (:host_id, :data_id, :group_id)");
while ($row = $DBRESULT->fetchRow()) {
- $request2 = "INSERT INTO centreon_acl (host_id, service_id, group_id) "
- . "VALUES ('" . $host_id . "', '" . $data["id"] . "', " . $row['group_id'] . ")";
- \CentreonDBInstance::getMonInstance()->query($request2);
+ $statement->bindValue(':host_id', (int) $host_id, \PDO::PARAM_INT);
+ $statement->bindValue(':data_id', (int) $data["id"], \PDO::PARAM_INT);
+ $statement->bindValue(':group_id', (int) $row['group_id'], \PDO::PARAM_INT);
+ $statement->execute();
}
}
}
From 4dabe5300e48fce47c6b95bb737cd76ae91cb6dc Mon Sep 17 00:00:00 2001
From: alaunois
Date: Mon, 1 Aug 2022 09:54:41 +0200
Subject: [PATCH 17/18] fix(conf) fix broker conf name display in listing
(#11372) (#11376)
---
.../configuration/configCentreonBroker/listCentreonBroker.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/www/include/configuration/configCentreonBroker/listCentreonBroker.php b/www/include/configuration/configCentreonBroker/listCentreonBroker.php
index 7e2e86df5ad..c8f025e3cce 100644
--- a/www/include/configuration/configCentreonBroker/listCentreonBroker.php
+++ b/www/include/configuration/configCentreonBroker/listCentreonBroker.php
@@ -169,7 +169,7 @@
$elemArr[$i] = array(
"MenuClass" => "list_" . $style,
"RowMenu_select" => $selectedElements->toHtml(),
- "RowMenu_name" => CentreonUtils::escapeSecure($config["config_name"]),
+ "RowMenu_name" => htmlentities($config["config_name"], ENT_QUOTES, 'UTF-8'),
"RowMenu_link" => "main.php?p=" . $p . "&o=c&id=" . $config['config_id'],
"RowMenu_desc" => CentreonUtils::escapeSecure(
substr(
From f5a745a9001984e8f1d76e27c8c12b1908a88f7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Duret?=
Date: Tue, 2 Aug 2022 11:57:22 +0200
Subject: [PATCH 18/18] fix(web): fix the comment deletion for host monitored
by poller (#11138)
Refs: MON-12828
---
www/include/monitoring/comments/comments.php | 2 +-
www/include/monitoring/comments/common-Func.php | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/www/include/monitoring/comments/comments.php b/www/include/monitoring/comments/comments.php
index d9761619ea6..11bfee39976 100644
--- a/www/include/monitoring/comments/comments.php
+++ b/www/include/monitoring/comments/comments.php
@@ -78,7 +78,7 @@
if (!empty($select)) {
foreach ($select as $key => $value) {
$res = explode(';', urldecode($key));
- DeleteComment($res[0], [(int)$res[1] . ';' . (int)$res[2] => 'on']);
+ DeleteComment($res[0], [$res[1] . ';' . (int)$res[2] => 'on']);
}
}
} else {
diff --git a/www/include/monitoring/comments/common-Func.php b/www/include/monitoring/comments/common-Func.php
index 0cface9ab7b..2439ce74064 100644
--- a/www/include/monitoring/comments/common-Func.php
+++ b/www/include/monitoring/comments/common-Func.php
@@ -47,7 +47,6 @@ function DeleteComment($type = null, $hosts = [])
foreach ($hosts as $key => $value) {
$res = preg_split("/\;/", $key);
- $res[0] = filter_var($res[0] ?? 0, FILTER_VALIDATE_INT);
$res[1] = filter_var($res[1] ?? 0, FILTER_VALIDATE_INT);
write_command(" DEL_" . $type . "_COMMENT;" . $res[1], GetMyHostPoller($pearDB, $res[0]));
}