mirror of
https://github.com/Combodo/iTop.git
synced 2026-10-05 07:59:10 +02:00
Compare commits
7 Commits
3.3.0
...
support/3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a4bbfe5a9 | ||
|
|
31a4e1a0a0 | ||
|
|
de74103118 | ||
|
|
a2a00cb582 | ||
|
|
4d699f79e8 | ||
|
|
873cea1a76 | ||
|
|
adc2596b4a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,7 +33,6 @@ tests/*/vendor/*
|
||||
!/data/index.php
|
||||
!/data/web.config
|
||||
!/data/exclude.txt
|
||||
!/data/.compilation-symlinks
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
|
||||
@@ -109,10 +109,18 @@ $('#shortcut_rename_dlg').dialog({
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: ShortcutRenameOK},
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
$(this).dialog( "close" ); $(this).remove();
|
||||
} },
|
||||
{
|
||||
text: "$sCancelButtonLabel",
|
||||
click: function() {
|
||||
$(this).dialog( "close" ); $(this).remove();
|
||||
},
|
||||
'class': 'ibo-button ibo-is-alternative ibo-is-neutral action cancel'
|
||||
},
|
||||
{
|
||||
text: "$sOkButtonLabel",
|
||||
click: ShortcutRenameOK,
|
||||
'class': 'ibo-is-regular ibo-is-primary'
|
||||
},
|
||||
],
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
|
||||
@@ -1750,8 +1750,8 @@ class Config
|
||||
'security.disable_joined_classes_filter' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, scope filters aren\'t applied to joined classes or union classes not directly listed in the SELECT clause.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
@@ -1766,8 +1766,8 @@ class Config
|
||||
'security.disable_exec_forced_login_for_all_enpoints' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, when no delegated authentication module is defined, no login will be forced on modules exec endpoints',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
|
||||
@@ -1704,7 +1704,7 @@ class DBObjectSearch extends DBSearch
|
||||
* @return array|mixed|\SQLObjectQuery|null
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null, $bSelectOnlyIds = false)
|
||||
{
|
||||
// Hide objects that are not visible to the current user
|
||||
//
|
||||
@@ -1775,6 +1775,8 @@ class DBObjectSearch extends DBSearch
|
||||
$aContextData['aSelectExpr'] = $aSelectExpr;
|
||||
$sRawId .= $bGetCount;
|
||||
$aContextData['bGetCount'] = $bGetCount;
|
||||
$sRawId .= 'ids:'.($bSelectOnlyIds ? '1' : '0');
|
||||
$aContextData['bSelectOnlyIds'] = $bSelectOnlyIds;
|
||||
if (is_array($aSelectedClasses)) {
|
||||
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
|
||||
}
|
||||
@@ -1828,7 +1830,7 @@ class DBObjectSearch extends DBSearch
|
||||
if (!isset($oSQLQuery)) {
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oSearch);
|
||||
$oSQLQuery = $oSQLObjectQueryBuilder->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
$oSQLQuery = $oSQLObjectQueryBuilder->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $bSelectOnlyIds);
|
||||
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
||||
|
||||
if (self::$m_bQueryCacheEnabled) {
|
||||
@@ -1938,7 +1940,7 @@ class DBObjectSearch extends DBSearch
|
||||
$oSearch = $this;
|
||||
$aClassesToFilter = $this->GetSelectedClasses();
|
||||
|
||||
// Opt-in for joined classes filtering, otherwise only filter the selected class(es)
|
||||
// Joined classes filtering can be disabled through the configuration.
|
||||
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === false) {
|
||||
$aClassesToFilter = $this->GetJoinedClasses();
|
||||
}
|
||||
|
||||
@@ -995,7 +995,7 @@ abstract class DBSearch
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
public function MakeSelectQuery($aOrderBy = [], $aArgs = [], $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
|
||||
public function MakeSelectQuery($aOrderBy = [], $aArgs = [], $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true, $bSelectOnlyIds = false)
|
||||
{
|
||||
// Check the order by specification, and prefix with the class alias
|
||||
// and make sure that the ordering columns are going to be selected
|
||||
@@ -1036,7 +1036,7 @@ abstract class DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount);
|
||||
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, null, null, $bSelectOnlyIds);
|
||||
|
||||
if ($this->m_bNoContextParameters) {
|
||||
// Only internal parameters
|
||||
@@ -1120,7 +1120,7 @@ abstract class DBSearch
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
|
||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null, $bSelectOnlyIds = false)
|
||||
{
|
||||
$oSearch = $this->ApplyDataFilters();
|
||||
|
||||
@@ -1142,7 +1142,7 @@ abstract class DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
|
||||
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr, $bSelectOnlyIds);
|
||||
$oSQLQuery->SetSourceOQL($oSearch->ToOQL());
|
||||
|
||||
// Join to an additional table, if required...
|
||||
@@ -1177,7 +1177,8 @@ abstract class DBSearch
|
||||
$bGetCount,
|
||||
$aGroupByExpr = null,
|
||||
$aSelectedClasses = null,
|
||||
$aSelectExpr = null
|
||||
$aSelectExpr = null,
|
||||
$bSelectOnlyIds = false
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -527,10 +527,10 @@ class DBUnionSearch extends DBSearch
|
||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null, $bSelectOnlyIds = false)
|
||||
{
|
||||
if (count($this->aSearches) == 1) {
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $bSelectOnlyIds);
|
||||
}
|
||||
|
||||
$aSQLQueries = [];
|
||||
@@ -610,7 +610,7 @@ class DBUnionSearch extends DBSearch
|
||||
}
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr, $bSelectOnlyIds);
|
||||
if (count($aSearchAliases) > 1) {
|
||||
// Necessary to make sure that selected columns will match throughout all the queries
|
||||
// (default order of selected fields depending on the order of JOINS)
|
||||
@@ -683,7 +683,7 @@ class DBUnionSearch extends DBSearch
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Opt-in for joined classes filtering, otherwise fallback on DBSearch filtering
|
||||
// Joined classes filtering can be disabled through the configuration.
|
||||
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === true) {
|
||||
return parent::ApplyDataFilters();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,13 +29,14 @@ class SQLObjectQueryBuilder
|
||||
* @param array $aGroupByExpr
|
||||
* @param array $aSelectedClasses
|
||||
* @param array $aSelectExpr
|
||||
* @param bool $bSelectOnlyIds
|
||||
*
|
||||
* @return null|SQLObjectQuery
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
public function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null, $bSelectOnlyIds = false)
|
||||
{
|
||||
if ($bGetCount || !is_null($aGroupByExpr)) {
|
||||
if ($bGetCount || !is_null($aGroupByExpr) || $bSelectOnlyIds) {
|
||||
// Avoid adding all the fields for counts or "group by" requests
|
||||
$aAttToLoad = [];
|
||||
foreach ($this->oDBObjetSearch->GetSelectedClasses() as $sClassAlias => $sClass) {
|
||||
|
||||
@@ -56,7 +56,7 @@ function RenderAttachments(AjaxPage $oPage, $iTransactionId)
|
||||
try {
|
||||
require_once APPROOT.'/application/startup.inc.php';
|
||||
require_once APPROOT.'/application/loginwebpage.class.inc.php';
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
LoginWebPage::DoLogin(); // No user portal should access this endpoint.
|
||||
|
||||
$oPage = new AjaxPage("");
|
||||
|
||||
@@ -105,15 +105,6 @@ try {
|
||||
$oPage->SetData($aResult);
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$iAttachmentId = utils::ReadParam('att_id', '');
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id = :id");
|
||||
$oSet = new DBObjectSet($oSearch, [], ['id' => $iAttachmentId]);
|
||||
while ($oAttachment = $oSet->Fetch()) {
|
||||
$oAttachment->DBDelete();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'refresh_attachments_render':
|
||||
$sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id');
|
||||
RenderAttachments($oPage, $sTempId);
|
||||
|
||||
@@ -1239,13 +1239,32 @@ JS
|
||||
$oSearch = new DBObjectSearch('Shortcut');
|
||||
$aShortcuts = utils::ReadMultipleSelection($oSearch);
|
||||
$iShortcut = $aShortcuts[0];
|
||||
$oShortcut = MetaModel::GetObject('Shortcut', $iShortcut);
|
||||
$oShortcutSearch = new DBObjectSearch('Shortcut');
|
||||
$oShortcutSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oShortcutSearch->AddCondition('id', $iShortcut, '=');
|
||||
$oShortcutSet = new CMDBObjectSet($oShortcutSearch);
|
||||
|
||||
$oShortcut = $oShortcutSet->Fetch();
|
||||
|
||||
if ($oShortcut === null) {
|
||||
throw new SecurityException(Dict::S('UI:ObjectDoesNotExist'));
|
||||
}
|
||||
|
||||
$oShortcut->StartRenameDialog($oPage);
|
||||
break;
|
||||
|
||||
case 'shortcut_rename_go':
|
||||
$iShortcut = utils::ReadParam('id', 0);
|
||||
$oShortcut = MetaModel::GetObject('Shortcut', $iShortcut);
|
||||
|
||||
$oShortcutSearch = new DBObjectSearch('Shortcut');
|
||||
$oShortcutSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oShortcutSearch->AddCondition('id', $iShortcut, '=');
|
||||
$oShortcutSet = new CMDBObjectSet($oShortcutSearch);
|
||||
$oShortcut = $oShortcutSet->Fetch();
|
||||
|
||||
if ($oShortcut === null) {
|
||||
throw new SecurityException(Dict::S('UI:ObjectDoesNotExist'));
|
||||
}
|
||||
|
||||
$sName = utils::ReadParam('attr_name', '', false, 'raw_data');
|
||||
if (strlen($sName) > 0) {
|
||||
|
||||
@@ -243,7 +243,7 @@ JS
|
||||
);
|
||||
$oShortcutsToolBar->AddSubBlock($oShortcutsRenameButton);
|
||||
// - Delete button
|
||||
$oShortcutsDeleteButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
||||
$oShortcutsDeleteButton = ButtonUIBlockFactory::MakeForDestructiveAction(
|
||||
Dict::S('UI:Button:Delete'),
|
||||
null,
|
||||
null,
|
||||
|
||||
@@ -111,7 +111,7 @@ class LoginWebPageTest extends ItopDataTestCase
|
||||
$this->assertStringContainsString('<title>iTop login</title>', $sPageContent, 'if itop is configured to force login when no there is no delegated authentication endpoints list, then login should be required.');
|
||||
}
|
||||
|
||||
public function testWithoutDelegatedAuthenticationEndpointsListWithDefaultConfiguration()
|
||||
public function testWithoutDelegatedAuthenticationEndpointsListRequiresLoginByDefault()
|
||||
{
|
||||
$sPageContent = $this->CallItopUri(
|
||||
"pages/exec.php?exec_module=extension-without-delegated-authentication-endpoints-list&exec_page=src/Controller/File.php",
|
||||
@@ -120,7 +120,24 @@ class LoginWebPageTest extends ItopDataTestCase
|
||||
true
|
||||
);
|
||||
|
||||
$this->assertStringContainsString('Yo', $sPageContent, 'by default (until N°9343) if no delegated authentication endpoints list is defined, not logged in persons should access pages');
|
||||
$this->assertStringContainsString('<title>iTop login</title>', $sPageContent, 'by default, login should be required when no delegated authentication endpoints list is defined');
|
||||
}
|
||||
|
||||
public function testWithoutDelegatedAuthenticationEndpointsListWithCompatibilityOptOut()
|
||||
{
|
||||
@chmod($this->oConfig->GetLoadedFile(), 0770);
|
||||
$this->oConfig->Set('security.disable_exec_forced_login_for_all_enpoints', true, 'AnythingButEmptyOrUnknownValue');
|
||||
$this->oConfig->WriteToFile();
|
||||
@chmod($this->oConfig->GetLoadedFile(), 0444);
|
||||
|
||||
$sPageContent = $this->CallItopUri(
|
||||
"pages/exec.php?exec_module=extension-without-delegated-authentication-endpoints-list&exec_page=src/Controller/File.php",
|
||||
[],
|
||||
[],
|
||||
true
|
||||
);
|
||||
|
||||
$this->assertStringContainsString('Yo !', $sPageContent, 'the compatibility opt-out should allow anonymous access when no delegated authentication endpoints list is defined');
|
||||
}
|
||||
|
||||
public function testNotInDelegatedAuthenticationEndpointsList()
|
||||
|
||||
@@ -419,6 +419,7 @@ class UserLocalTest extends ItopDataTestCase
|
||||
$this->assertInstanceOf(ormLinkSet::class, $oProfilesSet);
|
||||
$this->assertEquals(0, $oProfilesSet->Count());
|
||||
MetaModel::GetConfig()->Set('security.hide_administrators', false);
|
||||
MetaModel::GetConfig()->Set('security.disable_joined_classes_filter', true);
|
||||
$oProfilesSet = $this->GetAdminUserProfileList();
|
||||
$this->assertIsObject($oProfilesSet);
|
||||
$this->assertInstanceOf(ormLinkSet::class, $oProfilesSet);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\ItopAttachment;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class AttachmentAjaxEndpointTest extends ItopDataTestCase
|
||||
{
|
||||
public const USE_TRANSACTION = false;
|
||||
private const AUTHENTICATION_PASSWORD = 'tagada-Secret,007';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->BackupConfiguration();
|
||||
$this->AddLoginModeAndSaveConfiguration('url');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider AjaxEndpointAccessProvider
|
||||
*/
|
||||
public function testAjaxEndpointAccess(string $sProfile, int $iExpectedHttpCode): void
|
||||
{
|
||||
$sLogin = 'user-'.uniqid();
|
||||
$this->CreateUser($sLogin, self::$aURP_Profiles[$sProfile], self::AUTHENTICATION_PASSWORD);
|
||||
|
||||
$iHttpCode = $this->CallAttachmentEndpointAs($sLogin);
|
||||
|
||||
$this->assertSame($iExpectedHttpCode, $iHttpCode);
|
||||
}
|
||||
|
||||
public function AjaxEndpointAccessProvider(): array
|
||||
{
|
||||
return [
|
||||
'console user' => ['Service Desk Agent', 200],
|
||||
'portal user' => ['Portal user', 302], // redirect to portal
|
||||
];
|
||||
}
|
||||
|
||||
private function CallAttachmentEndpointAs(string $sLogin): int
|
||||
{
|
||||
$this->CallItopUri(
|
||||
'env-production/itop-attachments/ajax.itop-attachment.php?operation=add&'.http_build_query([
|
||||
'auth_user' => $sLogin,
|
||||
'auth_pwd' => self::AUTHENTICATION_PASSWORD,
|
||||
]),
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => ['X-Combodo-Ajax:1'],
|
||||
CURLOPT_POST => 0,
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
return $this->aLastCurlGetInfo['http_code'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user