Skip to content

Commit 62e09f1

Browse files
authored
N°8604 N°8605 - Add authentication to combodo-db-tools binaries (#817)
* N°8604 N°8605 - Add autoloader and dedicated classes for binaries utils * N°8605 - Harden security * N°8604 - Harden security * N°8604 N°8605 - Fixes from code review * N°8604 N°8605 - Improve robustness whether module is in datamodels/2.x or env-xxx folder
1 parent f82389d commit 62e09f1

18 files changed

+1356
-22
lines changed

datamodels/2.x/combodo-db-tools/bin/rebuildhk.php

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,87 @@
1010
*/
1111

1212
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
13+
use Combodo\iTop\DBTools\Enum\BinExitCode;
14+
use Combodo\iTop\DBTools\Exception\AuthenticationException;
1315

14-
require_once('../../../approot.inc.php');
16+
// env-xxx folders
17+
if (file_exists(__DIR__.'/../../../approot.inc.php')) {
18+
require_once __DIR__.'/../../../approot.inc.php';
19+
}
20+
// datamodel/2.x and data/xxx-modules folders
21+
elseif (file_exists(__DIR__.'/../../../../approot.inc.php')) {
22+
require_once __DIR__.'/../../../../approot.inc.php';
23+
}
1524
require_once APPROOT.'application/startup.inc.php';
1625

17-
foreach (MetaModel::GetClasses() as $sClass) {
18-
if (!MetaModel::HasTable($sClass)) {
19-
continue;
20-
}
26+
// Prepare output page
27+
$sPageTitle = "Database maintenance tools - Report";
28+
$bIsModeCLI = utils::IsModeCLI();
29+
if ($bIsModeCLI) {
30+
$oP = new CLIPage($sPageTitle);
31+
32+
SetupUtils::CheckPhpAndExtensionsForCli($oP, BinExitCode::FATAL->value);
33+
} else {
34+
$oP = new WebPage($sPageTitle);
35+
}
36+
37+
// Authentication logic
38+
try {
39+
utils::UseParamFile();
2140

22-
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
23-
// Check (once) all the attributes that are hierarchical keys
24-
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
25-
echo "Rebuild hierarchical key $sAttCode from $sClass.\n";
26-
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
41+
if ($bIsModeCLI) {
42+
$sAuthUser = utils::ReadParam('auth_user', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
43+
$sAuthPwd = utils::ReadParam('auth_pwd', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
44+
if (utils::IsNullOrEmptyString($sAuthUser) || utils::IsNullOrEmptyString($sAuthPwd)) {
45+
throw new AuthenticationException("Access credentials not provided, usage: php rebuildhk.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file_path>]");
2746
}
47+
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
48+
UserRights::Login($sAuthUser);
49+
} else {
50+
throw new AuthenticationException("Access wrong credentials ('$sAuthUser')");
51+
}
52+
} else {
53+
// Check user rights and prompt if needed
54+
LoginWebPage::DoLoginEx(null, true);
2855
}
56+
57+
if (!UserRights::IsAdministrator()) {
58+
throw new AuthenticationException("Access restricted to administrators");
59+
}
60+
} catch (AuthenticationException $oException) {
61+
$oP->p($oException->getMessage());
62+
$oP->output();
63+
exit(BinExitCode::ERROR->value);
64+
} catch (Exception $oException) {
65+
$oP->p("Error: ".$oException->GetMessage());
66+
$oP->output();
67+
exit(BinExitCode::FATAL->value);
2968
}
3069

31-
echo "Done\n";
70+
// Business logic
71+
try {
72+
foreach (MetaModel::GetClasses() as $sClass) {
73+
if (!MetaModel::HasTable($sClass)) {
74+
continue;
75+
}
76+
77+
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
78+
// Check (once) all the attributes that are hierarchical keys
79+
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
80+
$oP->p("Rebuild hierarchical key $sAttCode from $sClass.");
81+
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
82+
}
83+
}
84+
}
85+
86+
$oP->p("Done");
87+
$oP->output();
88+
} catch (AuthenticationException $oException) {
89+
$oP->p($oException->getMessage());
90+
$oP->output();
91+
exit(BinExitCode::ERROR->value);
92+
} catch (Exception $oException) {
93+
$oP->p("Error: ".$oException->GetMessage());
94+
$oP->output();
95+
exit(BinExitCode::FATAL->value);
96+
}

datamodels/2.x/combodo-db-tools/bin/report.php

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,93 @@
55
* @license http://opensource.org/licenses/AGPL-3.0
66
*/
77

8+
use Combodo\iTop\DBTools\Enum\BinExitCode;
9+
use Combodo\iTop\DBTools\Exception\AuthenticationException;
810
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
911

10-
require_once('../../../approot.inc.php');
11-
require_once(APPROOT.'application/startup.inc.php');
12+
// env-xxx folders
13+
if (file_exists(__DIR__.'/../../../approot.inc.php')) {
14+
require_once __DIR__.'/../../../approot.inc.php';
15+
}
16+
// datamodel/2.x and data/xxx-modules folders
17+
elseif (file_exists(__DIR__.'/../../../../approot.inc.php')) {
18+
require_once __DIR__.'/../../../../approot.inc.php';
19+
}
20+
21+
require_once APPROOT.'application/startup.inc.php';
22+
require_once APPROOT.'application/loginwebpage.class.inc.php';
23+
24+
require_once __DIR__.'/../db_analyzer.class.inc.php';
25+
26+
// Prepare output page
27+
$sPageTitle = "Database maintenance tools - Report";
28+
$bIsModeCLI = utils::IsModeCLI();
29+
if ($bIsModeCLI) {
30+
$oP = new CLIPage($sPageTitle);
1231

13-
require_once('../db_analyzer.class.inc.php');
14-
require_once('../src/Service/DBAnalyzerUtils.php');
32+
SetupUtils::CheckPhpAndExtensionsForCli($oP, BinExitCode::FATAL->value);
33+
} else {
34+
$oP = new WebPage($sPageTitle);
35+
}
36+
37+
// Authentication logic
38+
try {
39+
utils::UseParamFile();
1540

16-
$oDBAnalyzer = new DatabaseAnalyzer(0);
17-
$aResults = $oDBAnalyzer->CheckIntegrity([]);
41+
if ($bIsModeCLI) {
42+
$sAuthUser = utils::ReadParam('auth_user', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
43+
$sAuthPwd = utils::ReadParam('auth_pwd', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
44+
if (utils::IsNullOrEmptyString($sAuthUser) || utils::IsNullOrEmptyString($sAuthPwd)) {
45+
throw new AuthenticationException("Access credentials not provided, usage: php report.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file_path>]");
46+
}
47+
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
48+
UserRights::Login($sAuthUser);
49+
} else {
50+
throw new AuthenticationException("Access wrong credentials ('$sAuthUser')");
51+
}
52+
} else {
53+
// Check user rights and prompt if needed
54+
LoginWebPage::DoLoginEx(null, true);
55+
}
1856

19-
if (empty($aResults)) {
20-
echo "Database OK\n";
21-
exit(0);
57+
if (!UserRights::IsAdministrator()) {
58+
throw new AuthenticationException("Access restricted to administrators");
59+
}
60+
} catch (AuthenticationException $oException) {
61+
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
62+
$oP->p($sExceptionMessage);
63+
$oP->output();
64+
exit(BinExitCode::ERROR->value);
65+
} catch (Exception $oException) {
66+
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
67+
$oP->p("Error: ".$sExceptionMessage);
68+
$oP->output();
69+
exit(BinExitCode::FATAL->value);
2270
}
2371

24-
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
72+
// Business logic
73+
try {
74+
$oDBAnalyzer = new DatabaseAnalyzer(0);
75+
$aResults = $oDBAnalyzer->CheckIntegrity([]);
76+
77+
if (empty($aResults)) {
78+
$oP->p("Database OK");
79+
$oP->output();
80+
exit(BinExitCode::SUCCESS->value);
81+
}
2582

26-
echo "Report generated: {$sReportFile}.log\n";
83+
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
84+
85+
$oP->p("Report generated: {$sReportFile}.log");
86+
$oP->output();
87+
} catch (AuthenticationException $oException) {
88+
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
89+
$oP->p($sExceptionMessage);
90+
$oP->output();
91+
exit(BinExitCode::ERROR->value);
92+
} catch (Exception $oException) {
93+
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
94+
$oP->p("Error: ".$sExceptionMessage);
95+
$oP->output();
96+
exit(BinExitCode::FATAL->value);
97+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "combodo/combodo-db-tools",
3+
"license": "AGPL-3.0-only",
4+
"autoload": {
5+
"psr-4": {
6+
"Combodo\\iTop\\DBTools\\": "src/"
7+
}
8+
}
9+
}

datamodels/2.x/combodo-db-tools/composer.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
// Components
4444
//
4545
'datamodel' => [
46+
'vendor/autoload.php',
4647
'src/Service/DBToolsUtils.php',
4748
'src/Service/DBAnalyzerUtils.php',
4849
],
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* @copyright Copyright (C) 2010-2026 Combodo SAS
5+
* @license http://opensource.org/licenses/AGPL-3.0
6+
*/
7+
8+
namespace Combodo\iTop\DBTools\Enum;
9+
10+
/**
11+
* Enum for the exit codes of the bin scripts
12+
*/
13+
enum BinExitCode: int
14+
{
15+
case SUCCESS = 0;
16+
case ERROR = -1;
17+
case FATAL = -2;
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/*
3+
* @copyright Copyright (C) 2010-2026 Combodo SAS
4+
* @license http://opensource.org/licenses/AGPL-3.0
5+
*/
6+
7+
namespace Combodo\iTop\DBTools\Exception;
8+
9+
class AuthenticationException extends \Exception
10+
{
11+
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
// autoload.php @generated by Composer
4+
5+
if (PHP_VERSION_ID < 50600) {
6+
if (!headers_sent()) {
7+
header('HTTP/1.1 500 Internal Server Error');
8+
}
9+
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
10+
if (!ini_get('display_errors')) {
11+
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
12+
fwrite(STDERR, $err);
13+
} elseif (!headers_sent()) {
14+
echo $err;
15+
}
16+
}
17+
throw new RuntimeException($err);
18+
}
19+
20+
require_once __DIR__ . '/composer/autoload_real.php';
21+
22+
return ComposerAutoloaderInit38292b9b3a56c6c8776285a17c58034e::getLoader();

0 commit comments

Comments
 (0)