From d8c7888eacf8a90ee7395c267e731aab54c2c85f Mon Sep 17 00:00:00 2001 From: odain Date: Tue, 13 Jun 2023 15:39:04 +0200 Subject: [PATCH 1/8] =?UTF-8?q?N=C2=B05324=20-=20[ERGO]=20Avoid=20to=20hav?= =?UTF-8?q?e=20users=20with=20non-standalone=20power=20portal=20profile=20?= =?UTF-8?q?only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module.itop-profiles-itil.php | 1 + .../src/UserProfilesEventListener.php | 93 +++++ tests/php-unit-tests/ItopDataTestCase.php | 3 +- .../unitary-tests/core/UserRightsTest.php | 325 ++++++++++++++++++ 4 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php diff --git a/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php b/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php index 9f91ca4bc8..8f58ee2c41 100755 --- a/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php +++ b/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php @@ -36,6 +36,7 @@ SetupWebPage::AddModule( // Components // 'datamodel' => array( + 'src/UserProfilesEventListener.php' ), 'webservice' => array( //'webservices.itop-profiles-itil.php', diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php new file mode 100644 index 0000000000..6d63ddb4ad --- /dev/null +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -0,0 +1,93 @@ +Get('object'); + + try { + self::RepairProfiles($oUser); + } catch (Exception $oException) { + IssueLog::Error('Exception occurred on OnUserProfileLinkChange', LogChannels::DM_CRUD, [ + 'user_class' => get_class($oUser), + 'user_id' => $oUser->GetKey(), + 'exception_message' => $oException->getMessage(), + 'exception_stacktrace' => $oException->getTraceAsString(), + ]); + } + } + + public static function RepairProfiles(\User $oUser) : void + { + if (!is_null($oUser)) + { + $oCurrentUserProfileSet = $oUser->Get('profile_list'); + if ($oCurrentUserProfileSet->Count() === 1){ + $oProfile = $oCurrentUserProfileSet->Fetch(); + + if (POWER_USER_PORTAL_PROFILE_NAME === $oProfile->Get('profile')){ + //add portal user + // power portal user is not a standalone profile (it will break console UI) + $sOQL = sprintf("SELECT URP_Profiles WHERE name = '%s'", PORTAL_PROFILE_NAME); + $oSearch = \DBSearch::FromOQL($sOQL); + $oSearch->AllowAllData(); + $oSet = new \DBObjectSet($oSearch); + if ($oSet->Count() !==1){ + return; + } + + $oUserPortalProfile = $oSet->Fetch(); + $oUserProfile = new \URP_UserProfile(); + $oUserProfile->Set('profileid', $oUserPortalProfile->GetKey()); + $oCurrentUserProfileSet->AddItem($oUserProfile); + $oUser->Set('profile_list', $oCurrentUserProfileSet); + } + } + } + } + +} diff --git a/tests/php-unit-tests/ItopDataTestCase.php b/tests/php-unit-tests/ItopDataTestCase.php index d19c0a5cbd..657521ef95 100644 --- a/tests/php-unit-tests/ItopDataTestCase.php +++ b/tests/php-unit-tests/ItopDataTestCase.php @@ -48,6 +48,7 @@ use Server; use TagSetFieldData; use Ticket; use URP_UserProfile; +use User; use UserRequest; use VirtualHost; use VirtualMachine; @@ -482,7 +483,7 @@ class ItopDataTestCase extends ItopTestCase /** @var \ormLinkSet $oSet */ $oSet = $oUser->Get('profile_list'); $oSet->AddItem($oUserProfile); - $oUser = $this->updateObject('UserLocal', $oUser->GetKey(), array( + $oUser = $this->updateObject(User::class, $oUser->GetKey(), array( 'profile_list' => $oSet, )); $this->debug("Updated {$oUser->GetName()} ({$oUser->GetKey()})"); diff --git a/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php b/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php index 0b4cc29e48..80993cd4ea 100644 --- a/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php +++ b/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php @@ -26,6 +26,7 @@ namespace Combodo\iTop\Test\UnitTest\Core; +use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenuFactory; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use CoreCannotSaveObjectException; use CoreException; @@ -552,4 +553,328 @@ class UserRightsTest extends ItopDataTestCase ['Person', 'team_list', false], ]; } + + public function PortaPowerUserProvider(){ + return [ + 'Portal power user only => user should be repaired by adding User portal profile' => [ + 'aAssociatedProfilesBeforeUserCreation' => [ + 'Portal power user' + ], + 'aExpectedAssociatedProfilesAfterUserCreation'=> [ + 'Portal power user', + 'Portal user', + ] + ], + 'Portal power user + Support Agent => profiles untouched' => [ + 'aAssociatedProfilesBeforeUserCreation' => [ + 'Portal power user', + 'Support Agent', + ], + 'aExpectedAssociatedProfilesAfterUserCreation'=> [ + 'Portal power user', + 'Support Agent', + ] + ], + ]; + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLocalCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLocalUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLDAPCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLDAP(); + $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLDAPUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLDAP(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserExternalCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserExternalUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function CreateUserForProfileTesting(\User $oUserToCreate, array $aAssociatedProfilesBeforeUserCreation) : array + { + $aProfiles = []; + $oSearch = \DBSearch::FromOQL("SELECT URP_Profiles"); + $oProfileSet = new DBObjectSet($oSearch); + while (($oProfile = $oProfileSet->Fetch()) != null){ + $aProfiles[$oProfile->Get('name')] = $oProfile; + } + + $this->CreateTestOrganization(); + $oContact = $this->CreatePerson("1"); + $iContactid = $oContact->GetKey(); + + $oUserToCreate->Set('contactid', $iContactid); + $sUserClass = get_class($oUserToCreate); + + $oUserProfileList = $oUserToCreate->Get('profile_list'); + foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ + $oUserProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oUserProfile->Set('profileid', $oProfile->GetKey()); + $oUserProfile->Set('reason', 'UNIT Tests'); + $oUserProfileList->AddItem($oUserProfile); + } + + $oUserToCreate->Set('profile_list', $oUserProfileList); + $sId = $oUserToCreate->DBInsert(); + + return [ $sId, $aProfiles]; + } + + public function commonUserCreation($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $sUserClass = get_class($oUserToCreate); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, $aAssociatedProfilesBeforeUserCreation); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation){ + $oUser = \MetaModel::GetObject($sUserClass, $sId); + $oUserProfileList = $oUser->Get('profile_list'); + $aProfilesAfterCreation=[]; + while (($oProfile = $oUserProfileList->Fetch()) != null){ + $aProfilesAfterCreation[] = $oProfile->Get('profile'); + } + + foreach ($aExpectedAssociatedProfilesAfterUserCreation as $sExpectedProfileName){ + $this->assertTrue(in_array($sExpectedProfileName, $aProfilesAfterCreation), + "profile \'$sExpectedProfileName\' should be asociated to user after creation. " . var_export($aProfilesAfterCreation, true) ); + } + + $_SESSION = []; + + //$this->expectException(\Exception::class); + UserRights::Login($oUser->Get('login')); + + if (! UserRights::IsPortalUser()) { + //calling this API triggers Fatal Error on below OQL used by \User->GetContactObject() for a user with only 'portal power user' profile + /** + * Error: No result for the single row query: 'SELECT DISTINCT `Contact`.`id` AS `Contactid`, `Contact`.`name` AS `Contactname`, `Contact`.`status` AS `Contactstatus`, `Contact`.`org_id` AS `Contactorg_id`, `Organization_org_id`.`name` AS `Contactorg_name`, `Contact`.`email` AS `Contactemail`, `Contact`.`phone` AS `Contactphone`, `Contact`.`notify` AS `Contactnotify`, `Contact`.`function` AS `Contactfunction`, `Contact`.`finalclass` AS `Contactfinalclass`, IF((`Contact`.`finalclass` IN ('Team', 'Contact')), CAST(CONCAT(COALESCE(`Contact`.`name`, '')) AS CHAR), CAST(CONCAT(COALESCE(`Contact_poly_Person`.`first_name`, ''), COALESCE(' ', ''), COALESCE(`Contact`.`name`, '')) AS CHAR)) AS `Contactfriendlyname`, COALESCE((`Contact`.`status` = 'inactive'), 0) AS `Contactobsolescence_flag`, `Contact`.`obsolescence_date` AS `Contactobsolescence_date`, CAST(CONCAT(COALESCE(`Organization_org_id`.`name`, '')) AS CHAR) AS `Contactorg_id_friendlyname`, COALESCE((`Organization_org_id`.`status` = 'inactive'), 0) AS `Contactorg_id_obsolescence_flag` FROM `contact` AS `Contact` INNER JOIN `organization` AS `Organization_org_id` ON `Contact`.`org_id` = `Organization_org_id`.`id` LEFT JOIN `person` AS `Contact_poly_Person` ON `Contact`.`id` = `Contact_poly_Person`.`id` WHERE ((`Contact`.`id` = 40) AND 0) '. + */ + NavigationMenuFactory::MakeStandard(); + } + + $this->assertTrue(true, 'after fix N°5324 no exception raised'); + // logout + $_SESSION = []; + } + + public function commonUserUpdate($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $sUserClass = get_class($oUserToCreate); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, ["Administrator"]); + + $oUserToUpdate = \MetaModel::GetObject($sUserClass, $sId); + $oProfileList = $oUserToUpdate->Get('profile_list'); + while($oObj = $oProfileList->Fetch()){ + $oProfileList->RemoveItem($oObj->GetKey()); + } + + foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ + $oAdminUrpProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); + $oAdminUrpProfile->Set('reason', 'UNIT Tests'); + $oProfileList->AddItem($oAdminUrpProfile); + } + + $oUserToUpdate->Set('profile_list', $oProfileList); + $oUserToUpdate->DBWrite(); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function testUpdateUserExternalProfilesViaLinks(){ + $aInitialProfiles = [ "Administrator", "Portal power user"]; + + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + + $sUserClass = get_class($oUser); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); + + $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); + $aProfilesToRemove = ["Administrator"]; + foreach ($aProfilesToRemove as $sProfileName){ + if (array_key_exists($sProfileName, $aURPUserProfileByUser)){ + $oURPUserProfile = $aURPUserProfileByUser[$sProfileName]; + $oURPUserProfile->DBDelete(); + } + } + + $aExpectedProfilesAfterUpdate = ["Portal power user", "Portal user"]; + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); + } + + public function BulkUpdateUserExternalProfilesViaLinksProvider(){ + return [ + 'user profiles REPAIR 1' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '-Administrator', + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'user profiles REPAIR 2' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '+Portal power user', + '-Administrator', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'user profiles REPAIR 3' => [ + "aInitialProfiles" => [ "Administrator", "Portal power user"], + "aOperation" => [ + '-Administrator', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'NOTHING DONE with 1 profile' => [ + "aInitialProfiles" => [ "Administrator", "Portal power user"], + "aOperation" => [ + '-Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Administrator"], + ], + 'NOTHING DONE with 2 profiles including power...' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Administrator", "Portal power user"], + ], + 'NOTHING DONE with 2 profiles including power again ...' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Portal user", "Portal power user"], + ], + ]; + } + + /** + * @dataProvider BulkUpdateUserExternalProfilesViaLinksProvider + */ + public function testBulkUpdateUserExternalProfilesViaLinks($aInitialProfiles, $aOperation, $aExpectedProfilesAfterUpdate){ + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + + $sUserClass = get_class($oUser); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); + + \cmdbAbstractObject::SetEventDBLinksChangedBlocked(true); + + $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); + foreach ($aOperation as $sOperation){ + $sOp = substr($sOperation,0, 1); + $sProfileName = substr($sOperation,1); + + if ($sOp === "-"){ + if (array_key_exists($sProfileName, $aURPUserProfileByUser)){ + $oURPUserProfile = $aURPUserProfileByUser[$sProfileName]; + $oURPUserProfile->DBDelete(); + } + } else { + $oAdminUrpProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); + $oAdminUrpProfile->Set('userid', $sId); + $oAdminUrpProfile->DBInsert(); + } + } + + \cmdbAbstractObject::SetEventDBLinksChangedBlocked(false); + \cmdbAbstractObject::FireEventDbLinksChangedForAllObjects(); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); + } + + private function GetURPUserProfileByUser($iUserId) : array { + $aRes = []; + $oSearch = \DBSearch::FromOQL("SELECT URP_UserProfile WHERE userid=$iUserId"); + $oSet = new DBObjectSet($oSearch); + while (($oURPUserProfile = $oSet->Fetch()) != null){ + $aRes[$oURPUserProfile->Get('profile')] = $oURPUserProfile; + } + + return $aRes; + } } From b8d04e40e4567455adc44aaeadbebd44cdef72cc Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 14 Jun 2023 15:13:29 +0200 Subject: [PATCH 2/8] =?UTF-8?q?N=C2=B05324=20-=20repair=20profiles=20by=20?= =?UTF-8?q?default=20or=20if=20configured=20for=20customer=20with=20multip?= =?UTF-8?q?le=20profiles=20+=20move=20dedicated=20test=20in=20UserProfiles?= =?UTF-8?q?EventListenerTest.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/UserProfilesEventListener.php | 91 +++- .../unitary-tests/core/UserRightsTest.php | 325 ------------- .../UserProfilesEventListenerTest.php | 448 ++++++++++++++++++ 3 files changed, 523 insertions(+), 341 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index 6d63ddb4ad..0f299788b1 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -24,11 +24,20 @@ define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user'); */ class UserProfilesEventListener implements iEventServiceSetup { + private $oUserPortalProfile; + private $bIsRepairmentEnabled = false; + /** * @inheritDoc */ public function RegisterEventsAndListeners() { + $this->Init(); + + if (false === $this->bIsRepairmentEnabled){ + return; + } + $callback = [$this, 'OnUserProfileLinkChange']; $aEventSource = [\User::class, \UserExternal::class, \UserInternal::class]; @@ -45,23 +54,82 @@ class UserProfilesEventListener implements iEventServiceSetup ); } + public function IsRepairmentEnabled() : bool + { + return $this->bIsRepairmentEnabled; + } + public function OnUserProfileLinkChange(EventData $oEventData): void { /** @var \User $oObject */ $oUser = $oEventData->Get('object'); try { - self::RepairProfiles($oUser); - } catch (Exception $oException) { - IssueLog::Error('Exception occurred on OnUserProfileLinkChange', LogChannels::DM_CRUD, [ + $this->RepairProfiles($oUser); + } catch (Exception $e) { + IssueLog::Error('Exception occurred on RepairProfiles', LogChannels::DM_CRUD, [ 'user_class' => get_class($oUser), 'user_id' => $oUser->GetKey(), - 'exception_message' => $oException->getMessage(), - 'exception_stacktrace' => $oException->getTraceAsString(), + 'exception_message' => $e->getMessage(), + 'exception_stacktrace' => $e->getTraceAsString(), ]); } } - public static function RepairProfiles(\User $oUser) : void + /** + * @param $aPortalDispatcherData: passed only for testing purpose + * + * @return void + * @throws \ConfigException + * @throws \CoreException + */ + public function Init($aPortalDispatcherData=null) : void { + if (is_null($aPortalDispatcherData)){ + $aPortalDispatcherData = \PortalDispatcherData::GetData(); + } + + $sRepairmentProfile = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', null); + + if (is_null($sRepairmentProfile) && sizeof($aPortalDispatcherData) > 2){ + //when there are further portals we dont want to force a specific portal by repairing the associated profiles to a user + $this->bIsRepairmentEnabled = false; + return; + } + + if (is_null($sRepairmentProfile)){ + $sRepairmentProfile = PORTAL_PROFILE_NAME; + } + + try { + $sOQL = sprintf("SELECT URP_Profiles WHERE name = '%s'", $sRepairmentProfile); + $oSearch = \DBSearch::FromOQL($sOQL); + $oSearch->AllowAllData(); + $oSet = new \DBObjectSet($oSearch); + if ($oSet->Count() !== 1) { + //user portal profile does not exist + //current iTop is customized enough to disable repairment + $this->bIsRepairmentEnabled = false; + return; + } + + $this->oUserPortalProfile = $oSet->Fetch(); + if (is_null($this->oUserPortalProfile)){ + //may be not required. preventive code to disable repairment + $this->bIsRepairmentEnabled = false; + return; + } + } catch (\Exception $e) { + IssueLog::Error('Exception when searching user portal profile', LogChannels::DM_CRUD, [ + 'exception_message' => $e->getMessage(), + 'exception_stacktrace' => $e->getTraceAsString(), + ]); + $this->bIsRepairmentEnabled = false; + return; + } + + $this->bIsRepairmentEnabled = true; + } + + public function RepairProfiles(\User $oUser) : void { if (!is_null($oUser)) { @@ -72,17 +140,8 @@ class UserProfilesEventListener implements iEventServiceSetup if (POWER_USER_PORTAL_PROFILE_NAME === $oProfile->Get('profile')){ //add portal user // power portal user is not a standalone profile (it will break console UI) - $sOQL = sprintf("SELECT URP_Profiles WHERE name = '%s'", PORTAL_PROFILE_NAME); - $oSearch = \DBSearch::FromOQL($sOQL); - $oSearch->AllowAllData(); - $oSet = new \DBObjectSet($oSearch); - if ($oSet->Count() !==1){ - return; - } - - $oUserPortalProfile = $oSet->Fetch(); $oUserProfile = new \URP_UserProfile(); - $oUserProfile->Set('profileid', $oUserPortalProfile->GetKey()); + $oUserProfile->Set('profileid', $this->oUserPortalProfile->GetKey()); $oCurrentUserProfileSet->AddItem($oUserProfile); $oUser->Set('profile_list', $oCurrentUserProfileSet); } diff --git a/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php b/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php index 80993cd4ea..0b4cc29e48 100644 --- a/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php +++ b/tests/php-unit-tests/unitary-tests/core/UserRightsTest.php @@ -26,7 +26,6 @@ namespace Combodo\iTop\Test\UnitTest\Core; -use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenuFactory; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use CoreCannotSaveObjectException; use CoreException; @@ -553,328 +552,4 @@ class UserRightsTest extends ItopDataTestCase ['Person', 'team_list', false], ]; } - - public function PortaPowerUserProvider(){ - return [ - 'Portal power user only => user should be repaired by adding User portal profile' => [ - 'aAssociatedProfilesBeforeUserCreation' => [ - 'Portal power user' - ], - 'aExpectedAssociatedProfilesAfterUserCreation'=> [ - 'Portal power user', - 'Portal user', - ] - ], - 'Portal power user + Support Agent => profiles untouched' => [ - 'aAssociatedProfilesBeforeUserCreation' => [ - 'Portal power user', - 'Support Agent', - ], - 'aExpectedAssociatedProfilesAfterUserCreation'=> [ - 'Portal power user', - 'Support Agent', - ] - ], - ]; - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserLocalCreation($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserLocal(); - $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $oUser->Set('password', 'ABCD1234@gabuzomeu'); - $oUser->Set('language', 'EN US'); - $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserLocalUpdate($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserLocal(); - $sLogin = 'testUserLocalUpdateWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $oUser->Set('password', 'ABCD1234@gabuzomeu'); - $oUser->Set('language', 'EN US'); - $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserLDAPCreation($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserLDAP(); - $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserLDAPUpdate($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserLDAP(); - $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserExternalCreation($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserExternal(); - $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - /** - * @since 3.1.0 N°5324 - * @dataProvider PortaPowerUserProvider - */ - public function testUserExternalUpdate($aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $oUser = new \UserExternal(); - $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); - } - - public function CreateUserForProfileTesting(\User $oUserToCreate, array $aAssociatedProfilesBeforeUserCreation) : array - { - $aProfiles = []; - $oSearch = \DBSearch::FromOQL("SELECT URP_Profiles"); - $oProfileSet = new DBObjectSet($oSearch); - while (($oProfile = $oProfileSet->Fetch()) != null){ - $aProfiles[$oProfile->Get('name')] = $oProfile; - } - - $this->CreateTestOrganization(); - $oContact = $this->CreatePerson("1"); - $iContactid = $oContact->GetKey(); - - $oUserToCreate->Set('contactid', $iContactid); - $sUserClass = get_class($oUserToCreate); - - $oUserProfileList = $oUserToCreate->Get('profile_list'); - foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ - $oUserProfile = new URP_UserProfile(); - $oProfile = $aProfiles[$sProfileName]; - $oUserProfile->Set('profileid', $oProfile->GetKey()); - $oUserProfile->Set('reason', 'UNIT Tests'); - $oUserProfileList->AddItem($oUserProfile); - } - - $oUserToCreate->Set('profile_list', $oUserProfileList); - $sId = $oUserToCreate->DBInsert(); - - return [ $sId, $aProfiles]; - } - - public function commonUserCreation($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $sUserClass = get_class($oUserToCreate); - list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, $aAssociatedProfilesBeforeUserCreation); - - $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); - } - - public function CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation){ - $oUser = \MetaModel::GetObject($sUserClass, $sId); - $oUserProfileList = $oUser->Get('profile_list'); - $aProfilesAfterCreation=[]; - while (($oProfile = $oUserProfileList->Fetch()) != null){ - $aProfilesAfterCreation[] = $oProfile->Get('profile'); - } - - foreach ($aExpectedAssociatedProfilesAfterUserCreation as $sExpectedProfileName){ - $this->assertTrue(in_array($sExpectedProfileName, $aProfilesAfterCreation), - "profile \'$sExpectedProfileName\' should be asociated to user after creation. " . var_export($aProfilesAfterCreation, true) ); - } - - $_SESSION = []; - - //$this->expectException(\Exception::class); - UserRights::Login($oUser->Get('login')); - - if (! UserRights::IsPortalUser()) { - //calling this API triggers Fatal Error on below OQL used by \User->GetContactObject() for a user with only 'portal power user' profile - /** - * Error: No result for the single row query: 'SELECT DISTINCT `Contact`.`id` AS `Contactid`, `Contact`.`name` AS `Contactname`, `Contact`.`status` AS `Contactstatus`, `Contact`.`org_id` AS `Contactorg_id`, `Organization_org_id`.`name` AS `Contactorg_name`, `Contact`.`email` AS `Contactemail`, `Contact`.`phone` AS `Contactphone`, `Contact`.`notify` AS `Contactnotify`, `Contact`.`function` AS `Contactfunction`, `Contact`.`finalclass` AS `Contactfinalclass`, IF((`Contact`.`finalclass` IN ('Team', 'Contact')), CAST(CONCAT(COALESCE(`Contact`.`name`, '')) AS CHAR), CAST(CONCAT(COALESCE(`Contact_poly_Person`.`first_name`, ''), COALESCE(' ', ''), COALESCE(`Contact`.`name`, '')) AS CHAR)) AS `Contactfriendlyname`, COALESCE((`Contact`.`status` = 'inactive'), 0) AS `Contactobsolescence_flag`, `Contact`.`obsolescence_date` AS `Contactobsolescence_date`, CAST(CONCAT(COALESCE(`Organization_org_id`.`name`, '')) AS CHAR) AS `Contactorg_id_friendlyname`, COALESCE((`Organization_org_id`.`status` = 'inactive'), 0) AS `Contactorg_id_obsolescence_flag` FROM `contact` AS `Contact` INNER JOIN `organization` AS `Organization_org_id` ON `Contact`.`org_id` = `Organization_org_id`.`id` LEFT JOIN `person` AS `Contact_poly_Person` ON `Contact`.`id` = `Contact_poly_Person`.`id` WHERE ((`Contact`.`id` = 40) AND 0) '. - */ - NavigationMenuFactory::MakeStandard(); - } - - $this->assertTrue(true, 'after fix N°5324 no exception raised'); - // logout - $_SESSION = []; - } - - public function commonUserUpdate($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) - { - $sUserClass = get_class($oUserToCreate); - list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, ["Administrator"]); - - $oUserToUpdate = \MetaModel::GetObject($sUserClass, $sId); - $oProfileList = $oUserToUpdate->Get('profile_list'); - while($oObj = $oProfileList->Fetch()){ - $oProfileList->RemoveItem($oObj->GetKey()); - } - - foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ - $oAdminUrpProfile = new URP_UserProfile(); - $oProfile = $aProfiles[$sProfileName]; - $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); - $oAdminUrpProfile->Set('reason', 'UNIT Tests'); - $oProfileList->AddItem($oAdminUrpProfile); - } - - $oUserToUpdate->Set('profile_list', $oProfileList); - $oUserToUpdate->DBWrite(); - - $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); - } - - public function testUpdateUserExternalProfilesViaLinks(){ - $aInitialProfiles = [ "Administrator", "Portal power user"]; - - $oUser = new \UserExternal(); - $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - - $sUserClass = get_class($oUser); - list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); - - $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); - $aProfilesToRemove = ["Administrator"]; - foreach ($aProfilesToRemove as $sProfileName){ - if (array_key_exists($sProfileName, $aURPUserProfileByUser)){ - $oURPUserProfile = $aURPUserProfileByUser[$sProfileName]; - $oURPUserProfile->DBDelete(); - } - } - - $aExpectedProfilesAfterUpdate = ["Portal power user", "Portal user"]; - $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); - } - - public function BulkUpdateUserExternalProfilesViaLinksProvider(){ - return [ - 'user profiles REPAIR 1' => [ - "aInitialProfiles" => [ "Administrator"], - "aOperation" => [ - '-Administrator', - '+Portal power user', - ], - "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], - ], - 'user profiles REPAIR 2' => [ - "aInitialProfiles" => [ "Administrator"], - "aOperation" => [ - '+Portal power user', - '-Administrator', - ], - "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], - ], - 'user profiles REPAIR 3' => [ - "aInitialProfiles" => [ "Administrator", "Portal power user"], - "aOperation" => [ - '-Administrator', - ], - "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], - ], - 'NOTHING DONE with 1 profile' => [ - "aInitialProfiles" => [ "Administrator", "Portal power user"], - "aOperation" => [ - '-Portal power user', - ], - "aExpectedProfilesAfterUpdate" => ["Administrator"], - ], - 'NOTHING DONE with 2 profiles including power...' => [ - "aInitialProfiles" => [ "Administrator"], - "aOperation" => [ - '+Portal power user', - ], - "aExpectedProfilesAfterUpdate" => ["Administrator", "Portal power user"], - ], - 'NOTHING DONE with 2 profiles including power again ...' => [ - "aInitialProfiles" => [ "Administrator"], - "aOperation" => [ - '+Portal power user', - ], - "aExpectedProfilesAfterUpdate" => ["Portal user", "Portal power user"], - ], - ]; - } - - /** - * @dataProvider BulkUpdateUserExternalProfilesViaLinksProvider - */ - public function testBulkUpdateUserExternalProfilesViaLinks($aInitialProfiles, $aOperation, $aExpectedProfilesAfterUpdate){ - $oUser = new \UserExternal(); - $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); - $oUser->Set('login', $sLogin); - - $sUserClass = get_class($oUser); - list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); - - \cmdbAbstractObject::SetEventDBLinksChangedBlocked(true); - - $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); - foreach ($aOperation as $sOperation){ - $sOp = substr($sOperation,0, 1); - $sProfileName = substr($sOperation,1); - - if ($sOp === "-"){ - if (array_key_exists($sProfileName, $aURPUserProfileByUser)){ - $oURPUserProfile = $aURPUserProfileByUser[$sProfileName]; - $oURPUserProfile->DBDelete(); - } - } else { - $oAdminUrpProfile = new URP_UserProfile(); - $oProfile = $aProfiles[$sProfileName]; - $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); - $oAdminUrpProfile->Set('userid', $sId); - $oAdminUrpProfile->DBInsert(); - } - } - - \cmdbAbstractObject::SetEventDBLinksChangedBlocked(false); - \cmdbAbstractObject::FireEventDbLinksChangedForAllObjects(); - - $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); - } - - private function GetURPUserProfileByUser($iUserId) : array { - $aRes = []; - $oSearch = \DBSearch::FromOQL("SELECT URP_UserProfile WHERE userid=$iUserId"); - $oSet = new DBObjectSet($oSearch); - while (($oURPUserProfile = $oSet->Fetch()) != null){ - $aRes[$oURPUserProfile->Get('profile')] = $oURPUserProfile; - } - - return $aRes; - } } diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php new file mode 100644 index 0000000000..6d020f6ded --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php @@ -0,0 +1,448 @@ + +// + +/** + * Created by PhpStorm. + * User: Eric + * Date: 25/01/2018 + * Time: 11:12 + */ + +namespace Combodo\iTop\Test\UnitTest\Module\iTopProfilesItil; + +use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenuFactory; +use Combodo\iTop\ItilProfiles\UserProfilesEventListener; +use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use DBObjectSet; +use URP_UserProfile; +use UserRights; + +/** + * @group itopRequestMgmt + * @group userRights + * @group defaultProfiles + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * @backupGlobals disabled + */ +class UserProfilesEventListenerTest extends ItopDataTestCase +{ + public function PortaPowerUserProvider(){ + return [ + 'Portal power user only => user should be repaired by adding User portal profile' => [ + 'aAssociatedProfilesBeforeUserCreation' => [ + 'Portal power user' + ], + 'aExpectedAssociatedProfilesAfterUserCreation'=> [ + 'Portal power user', + 'Portal user', + ] + ], + 'Portal power user + Support Agent => profiles untouched' => [ + 'aAssociatedProfilesBeforeUserCreation' => [ + 'Portal power user', + 'Support Agent', + ], + 'aExpectedAssociatedProfilesAfterUserCreation'=> [ + 'Portal power user', + 'Support Agent', + ] + ], + ]; + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLocalCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLocalUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLDAPCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLDAP(); + $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserLDAPUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserLDAP(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserExternalCreation($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + /** + * @since 3.1.0 N°5324 + * @dataProvider PortaPowerUserProvider + */ + public function testUserExternalUpdate($aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $this->commonUserUpdate($oUser, $aAssociatedProfilesBeforeUserCreation, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function CreateUserForProfileTesting(\User $oUserToCreate, array $aAssociatedProfilesBeforeUserCreation, $bDbInsert=true) : array + { + $aProfiles = []; + $oSearch = \DBSearch::FromOQL("SELECT URP_Profiles"); + $oProfileSet = new DBObjectSet($oSearch); + while (($oProfile = $oProfileSet->Fetch()) != null){ + $aProfiles[$oProfile->Get('name')] = $oProfile; + } + + $this->CreateTestOrganization(); + $oContact = $this->CreatePerson("1"); + $iContactid = $oContact->GetKey(); + + $oUserToCreate->Set('contactid', $iContactid); + $sUserClass = get_class($oUserToCreate); + + $oUserProfileList = $oUserToCreate->Get('profile_list'); + foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ + $oUserProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oUserProfile->Set('profileid', $oProfile->GetKey()); + $oUserProfile->Set('reason', 'UNIT Tests'); + $oUserProfileList->AddItem($oUserProfile); + } + + $oUserToCreate->Set('profile_list', $oUserProfileList); + if ($bDbInsert){ + $sId = $oUserToCreate->DBInsert(); + } else { + $sId = -1; + } + + return [ $sId, $aProfiles]; + } + + public function commonUserCreation($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $sUserClass = get_class($oUserToCreate); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, $aAssociatedProfilesBeforeUserCreation); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation){ + $oUser = \MetaModel::GetObject($sUserClass, $sId); + $oUserProfileList = $oUser->Get('profile_list'); + $aProfilesAfterCreation=[]; + while (($oProfile = $oUserProfileList->Fetch()) != null){ + $aProfilesAfterCreation[] = $oProfile->Get('profile'); + } + + foreach ($aExpectedAssociatedProfilesAfterUserCreation as $sExpectedProfileName){ + $this->assertTrue(in_array($sExpectedProfileName, $aProfilesAfterCreation), + "profile \'$sExpectedProfileName\' should be asociated to user after creation. " . var_export($aProfilesAfterCreation, true) ); + } + + $_SESSION = []; + + //$this->expectException(\Exception::class); + UserRights::Login($oUser->Get('login')); + + if (! UserRights::IsPortalUser()) { + //calling this API triggers Fatal Error on below OQL used by \User->GetContactObject() for a user with only 'portal power user' profile + /** + * Error: No result for the single row query: 'SELECT DISTINCT `Contact`.`id` AS `Contactid`, `Contact`.`name` AS `Contactname`, `Contact`.`status` AS `Contactstatus`, `Contact`.`org_id` AS `Contactorg_id`, `Organization_org_id`.`name` AS `Contactorg_name`, `Contact`.`email` AS `Contactemail`, `Contact`.`phone` AS `Contactphone`, `Contact`.`notify` AS `Contactnotify`, `Contact`.`function` AS `Contactfunction`, `Contact`.`finalclass` AS `Contactfinalclass`, IF((`Contact`.`finalclass` IN ('Team', 'Contact')), CAST(CONCAT(COALESCE(`Contact`.`name`, '')) AS CHAR), CAST(CONCAT(COALESCE(`Contact_poly_Person`.`first_name`, ''), COALESCE(' ', ''), COALESCE(`Contact`.`name`, '')) AS CHAR)) AS `Contactfriendlyname`, COALESCE((`Contact`.`status` = 'inactive'), 0) AS `Contactobsolescence_flag`, `Contact`.`obsolescence_date` AS `Contactobsolescence_date`, CAST(CONCAT(COALESCE(`Organization_org_id`.`name`, '')) AS CHAR) AS `Contactorg_id_friendlyname`, COALESCE((`Organization_org_id`.`status` = 'inactive'), 0) AS `Contactorg_id_obsolescence_flag` FROM `contact` AS `Contact` INNER JOIN `organization` AS `Organization_org_id` ON `Contact`.`org_id` = `Organization_org_id`.`id` LEFT JOIN `person` AS `Contact_poly_Person` ON `Contact`.`id` = `Contact_poly_Person`.`id` WHERE ((`Contact`.`id` = 40) AND 0) '. + */ + NavigationMenuFactory::MakeStandard(); + } + + $this->assertTrue(true, 'after fix N°5324 no exception raised'); + // logout + $_SESSION = []; + } + + public function commonUserUpdate($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, + $aExpectedAssociatedProfilesAfterUserCreation) + { + $sUserClass = get_class($oUserToCreate); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, ["Administrator"]); + + $oUserToUpdate = \MetaModel::GetObject($sUserClass, $sId); + $oProfileList = $oUserToUpdate->Get('profile_list'); + while($oObj = $oProfileList->Fetch()){ + $oProfileList->RemoveItem($oObj->GetKey()); + } + + foreach ($aAssociatedProfilesBeforeUserCreation as $sProfileName){ + $oAdminUrpProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); + $oAdminUrpProfile->Set('reason', 'UNIT Tests'); + $oProfileList->AddItem($oAdminUrpProfile); + } + + $oUserToUpdate->Set('profile_list', $oProfileList); + $oUserToUpdate->DBWrite(); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); + } + + public function testUpdateUserExternalProfilesViaLinks(){ + $aInitialProfiles = [ "Administrator", "Portal power user"]; + + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + + $sUserClass = get_class($oUser); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); + + $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); + $sProfileNameToRemove = "Administrator"; + if (array_key_exists($sProfileNameToRemove, $aURPUserProfileByUser)){ + $oURPUserProfile = $aURPUserProfileByUser[$sProfileNameToRemove]; + $oURPUserProfile->DBDelete(); + } + + $aExpectedProfilesAfterUpdate = ["Portal power user", "Portal user"]; + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); + } + + public function BulkUpdateUserExternalProfilesViaLinksProvider(){ + return [ + 'user profiles REPAIR 1' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '-Administrator', + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'user profiles REPAIR 2' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '+Portal power user', + '-Administrator', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'user profiles REPAIR 3' => [ + "aInitialProfiles" => [ "Administrator", "Portal power user"], + "aOperation" => [ + '-Administrator', + ], + "aExpectedProfilesAfterUpdate" => ["Portal power user", "Portal user"], + ], + 'NOTHING DONE with 1 profile' => [ + "aInitialProfiles" => [ "Administrator", "Portal power user"], + "aOperation" => [ + '-Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Administrator"], + ], + 'NOTHING DONE with 2 profiles including power...' => [ + "aInitialProfiles" => [ "Administrator"], + "aOperation" => [ + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Administrator", "Portal power user"], + ], + 'NOTHING DONE with 2 profiles including power again ...' => [ + "aInitialProfiles" => [ "Portal user"], + "aOperation" => [ + '+Portal power user', + ], + "aExpectedProfilesAfterUpdate" => ["Portal user", "Portal power user"], + ], + ]; + } + + /** + * @dataProvider BulkUpdateUserExternalProfilesViaLinksProvider + */ + public function testBulkUpdateUserExternalProfilesViaLinks($aInitialProfiles, $aOperation, $aExpectedProfilesAfterUpdate){ + $oUser = new \UserExternal(); + $sLogin = 'testUserLDAPUpdateWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + + $sUserClass = get_class($oUser); + list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUser, $aInitialProfiles); + + \cmdbAbstractObject::SetEventDBLinksChangedBlocked(true); + + $aURPUserProfileByUser = $this->GetURPUserProfileByUser($sId); + foreach ($aOperation as $sOperation){ + $sOp = substr($sOperation,0, 1); + $sProfileName = substr($sOperation,1); + + if ($sOp === "-"){ + if (array_key_exists($sProfileName, $aURPUserProfileByUser)){ + $oURPUserProfile = $aURPUserProfileByUser[$sProfileName]; + $oURPUserProfile->DBDelete(); + } + } else { + $oAdminUrpProfile = new URP_UserProfile(); + $oProfile = $aProfiles[$sProfileName]; + $oAdminUrpProfile->Set('profileid', $oProfile->GetKey()); + $oAdminUrpProfile->Set('userid', $sId); + $oAdminUrpProfile->DBInsert(); + } + } + + \cmdbAbstractObject::SetEventDBLinksChangedBlocked(false); + \cmdbAbstractObject::FireEventDbLinksChangedForAllObjects(); + + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedProfilesAfterUpdate); + } + + private function GetURPUserProfileByUser($iUserId) : array { + $aRes = []; + $oSearch = \DBSearch::FromOQL("SELECT URP_UserProfile WHERE userid=$iUserId"); + $oSet = new DBObjectSet($oSearch); + while (($oURPUserProfile = $oSet->Fetch()) != null){ + $aRes[$oURPUserProfile->Get('profile')] = $oURPUserProfile; + } + + return $aRes; + } + + public function testUserProfilesEventListenerInit_nominal(){ + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testUserProfilesEventListenerInit_furtherportals_norepairmentconfigured(){ + $aPortalDispatcherData = [ + 'itop-portal', + 'customer-portal', + 'backoffice' + ]; + + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init($aPortalDispatcherData); + + $this->assertFalse($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testUserProfilesEventListenerInit_furtherportals_repairmentconfigured(){ + $aPortalDispatcherData = [ + 'itop-portal', + 'customer-portal', + 'backoffice' + ]; + + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Portal user'); + + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init($aPortalDispatcherData); + + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testUserProfilesEventListenerInit_with_unknownprofile(){ + $aPortalDispatcherData = [ + 'itop-portal', + 'customer-portal', + 'backoffice' + ]; + + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Dummy Profile'); + + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init($aPortalDispatcherData); + + $this->assertFalse($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testRepairProfiles_WithAnotherFallbackProfile() + { + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Change Supervisor'); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + + $this->CreateUserForProfileTesting($oUser, ['Portal power user'], false); + $oUserProfilesEventListener->RepairProfiles($oUser); + + $oUserProfileList = $oUser->Get('profile_list'); + $aProfilesAfterCreation=[]; + while (($oProfile = $oUserProfileList->Fetch()) != null){ + $aProfilesAfterCreation[] = $oProfile->Get('profile'); + } + + $this->assertContains('Change Supervisor', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + $this->assertContains('Portal power user', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + } +} From c6b203fc4e8201e0aaed102b8ec694e2e91ab7b8 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 14 Jun 2023 15:14:55 +0200 Subject: [PATCH 3/8] =?UTF-8?q?N=C2=B05324=20-=20make=20EVENT=5FDB=5FLINKS?= =?UTF-8?q?=5FCHANGED=20event=20work=20with=20URP=5FUserProfile=20by=20add?= =?UTF-8?q?ing=20is-link=20property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/userrights/userrightsprofile.class.inc.php | 1 + addons/userrights/userrightsprofile.db.class.inc.php | 1 + addons/userrights/userrightsprojection.class.inc.php | 1 + core/datamodel.core.xml | 1 + 4 files changed, 4 insertions(+) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 610cb91837..e1406e1a44 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -220,6 +220,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI { $aParams = array ( + "is_link" => true, //since 3.1 N°5324 "category" => "addon/userrights,grant_by_profile,filter", "key_type" => "autoincrement", "name_attcode" => array("userlogin", "profile"), diff --git a/addons/userrights/userrightsprofile.db.class.inc.php b/addons/userrights/userrightsprofile.db.class.inc.php index 8b02989f43..1ba202c0c3 100644 --- a/addons/userrights/userrightsprofile.db.class.inc.php +++ b/addons/userrights/userrightsprofile.db.class.inc.php @@ -326,6 +326,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI { $aParams = array ( + "is_link" => true, //since 3.1 N°5324 "category" => "addon/userrights", "key_type" => "autoincrement", "name_attcode" => array("userlogin", "profile"), diff --git a/addons/userrights/userrightsprojection.class.inc.php b/addons/userrights/userrightsprojection.class.inc.php index 7a9642218a..5b70bd6ace 100644 --- a/addons/userrights/userrightsprojection.class.inc.php +++ b/addons/userrights/userrightsprojection.class.inc.php @@ -269,6 +269,7 @@ class URP_UserProfile extends UserRightsBaseClass { $aParams = array ( + "is_link" => true, //since 3.1 N°5324 "category" => "addon/userrights", "key_type" => "autoincrement", "name_attcode" => array("userlogin", "profile"), diff --git a/core/datamodel.core.xml b/core/datamodel.core.xml index 0a98b4da66..26a606a8cd 100644 --- a/core/datamodel.core.xml +++ b/core/datamodel.core.xml @@ -57,6 +57,7 @@ cmdbAbstractObject + 1 addon/userrights,grant_by_profile From cd3f7d7ead8fa0d08a01ee54b902bd52262acc57 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 21 Jun 2023 21:42:40 +0200 Subject: [PATCH 4/8] =?UTF-8?q?N=C2=B05324=20-=20disable=20repairment=20wi?= =?UTF-8?q?th=20backoffice=20and=20a=20customized=20portal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/UserProfilesEventListener.php | 9 ++++++- .../UserProfilesEventListenerTest.php | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index 0f299788b1..6ce5b3f797 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -89,10 +89,17 @@ class UserProfilesEventListener implements iEventServiceSetup $sRepairmentProfile = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', null); - if (is_null($sRepairmentProfile) && sizeof($aPortalDispatcherData) > 2){ + if (is_null($sRepairmentProfile) && count($aPortalDispatcherData) > 2){ //when there are further portals we dont want to force a specific portal by repairing the associated profiles to a user $this->bIsRepairmentEnabled = false; return; + } else{ + $aPortalNames = array_keys($aPortalDispatcherData); + sort($aPortalNames); + if ($aPortalNames !== ['backoffice', 'itop-portal']){ + $this->bIsRepairmentEnabled = false; + return; + } } if (is_null($sRepairmentProfile)){ diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php index 6d020f6ded..0be313ef1e 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php @@ -377,13 +377,27 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); } - public function testUserProfilesEventListenerInit_furtherportals_norepairmentconfigured(){ - $aPortalDispatcherData = [ - 'itop-portal', - 'customer-portal', - 'backoffice' + public function CustomizedPortalsProvider(){ + return [ + 'console + customized portal' => [ + 'aPortalDispatcherData' => [ + 'customer-portal', + 'backoffice' + ]], + 'console + itop portal + customized portal' => [ + 'aPortalDispatcherData' => [ + 'itop-portal', + 'customer-portal', + 'backoffice' + ] + ], ]; + } + /** + * @dataProvider CustomizedPortalsProvider + */ + public function testUserProfilesEventListenerInit_furtherportals_norepairmentconfigured($aPortalDispatcherData){ $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init($aPortalDispatcherData); From ad46d47e21b476f0bb3f67672322ed36140b8db6 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 21 Jun 2023 23:44:39 +0200 Subject: [PATCH 5/8] =?UTF-8?q?N=C2=B05324=20-=20repairing=20or=20warning?= =?UTF-8?q?=20profiles=20conf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/UserProfilesEventListener.php | 112 ++++++++++++------ .../UserProfilesEventListenerTest.php | 95 ++++++++++++++- 2 files changed, 171 insertions(+), 36 deletions(-) diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index 6ce5b3f797..2a0cf0986b 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -24,9 +24,12 @@ define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user'); */ class UserProfilesEventListener implements iEventServiceSetup { - private $oUserPortalProfile; + const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'poweruserportal-repair-profile'; private $bIsRepairmentEnabled = false; + //map: non standalone profile name => repairing profile id + private $aNonStandaloneProfilesMap = []; + /** * @inheritDoc */ @@ -87,13 +90,16 @@ class UserProfilesEventListener implements iEventServiceSetup $aPortalDispatcherData = \PortalDispatcherData::GetData(); } - $sRepairmentProfile = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', null); + $aNonStandaloneProfiles = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', self::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null); + + //when there are customized portals on an itop, choosing a specific profile means choosing which portal user will access + //in that case, itop administrator has to specify it via itop configuration. we dont use default profiles repairment otherwise + if (is_null($aNonStandaloneProfiles)){ + if (count($aPortalDispatcherData) > 2){ + $this->bIsRepairmentEnabled = false; + return; + } - if (is_null($sRepairmentProfile) && count($aPortalDispatcherData) > 2){ - //when there are further portals we dont want to force a specific portal by repairing the associated profiles to a user - $this->bIsRepairmentEnabled = false; - return; - } else{ $aPortalNames = array_keys($aPortalDispatcherData); sort($aPortalNames); if ($aPortalNames !== ['backoffice', 'itop-portal']){ @@ -102,28 +108,25 @@ class UserProfilesEventListener implements iEventServiceSetup } } - if (is_null($sRepairmentProfile)){ - $sRepairmentProfile = PORTAL_PROFILE_NAME; + if (is_null($aNonStandaloneProfiles)){ + //default configuration in the case there are no customized portals + $aNonStandaloneProfiles = [ POWER_USER_PORTAL_PROFILE_NAME => PORTAL_PROFILE_NAME ]; + } + + if (! is_array($aNonStandaloneProfiles)){ + \IssueLog::Error(sprintf("%s is badly configured. it should be an array.", self::USERPROFILE_REPAIR_ITOP_PARAM_NAME), null, [self::USERPROFILE_REPAIR_ITOP_PARAM_NAME => $aNonStandaloneProfiles]); + $this->bIsRepairmentEnabled = false; + return; + } + + if (empty($aNonStandaloneProfiles)){ + //feature specifically disabled in itop configuration + $this->bIsRepairmentEnabled = false; + return; } try { - $sOQL = sprintf("SELECT URP_Profiles WHERE name = '%s'", $sRepairmentProfile); - $oSearch = \DBSearch::FromOQL($sOQL); - $oSearch->AllowAllData(); - $oSet = new \DBObjectSet($oSearch); - if ($oSet->Count() !== 1) { - //user portal profile does not exist - //current iTop is customized enough to disable repairment - $this->bIsRepairmentEnabled = false; - return; - } - - $this->oUserPortalProfile = $oSet->Fetch(); - if (is_null($this->oUserPortalProfile)){ - //may be not required. preventive code to disable repairment - $this->bIsRepairmentEnabled = false; - return; - } + $this->FetchRepairingProfileIds($aNonStandaloneProfiles); } catch (\Exception $e) { IssueLog::Error('Exception when searching user portal profile', LogChannels::DM_CRUD, [ 'exception_message' => $e->getMessage(), @@ -136,6 +139,43 @@ class UserProfilesEventListener implements iEventServiceSetup $this->bIsRepairmentEnabled = true; } + public function FetchRepairingProfileIds(array $aNonStandaloneProfiles) : void { + $aProfilesToSearch = array_unique(array_values($aNonStandaloneProfiles)); + if(($iIndex = array_search(null, $aProfilesToSearch)) !== false) { + unset($aProfilesToSearch[$iIndex]); + } + + if (1 === count($aProfilesToSearch)){ + $sInCondition = sprintf('"%s"', array_pop($aProfilesToSearch)); + } else { + $sInCondition = sprintf('"%s"', implode('","', $aProfilesToSearch)); + } + + $sOql = "SELECT URP_Profiles WHERE name IN ($sInCondition)"; + $oSearch = \DBSearch::FromOQL($sOql); + $oSearch->AllowAllData(); + $oSet = new \DBObjectSet($oSearch); + $aProfiles = []; + while(($oProfile = $oSet->Fetch()) != null) { + $sProfileName = $oProfile->Get('name'); + $aProfiles[$sProfileName] = $oProfile->GetKey(); + } + + $this->aNonStandaloneProfilesMap = []; + foreach ($aNonStandaloneProfiles as $sNonStandaloneProfileName => $sRepairProfileName) { + if (is_null($sRepairProfileName)) { + $this->aNonStandaloneProfilesMap[$sNonStandaloneProfileName] = null; + continue; + } + + if (!array_key_exists($sRepairProfileName, $aProfiles)) { + throw new \Exception(sprintf("%s is badly configured. profile $sRepairProfileName does not exist.", self::USERPROFILE_REPAIR_ITOP_PARAM_NAME)); + } + + $this->aNonStandaloneProfilesMap[$sNonStandaloneProfileName] = $aProfiles[$sRepairProfileName]; + } + } + public function RepairProfiles(\User $oUser) : void { if (!is_null($oUser)) @@ -143,14 +183,20 @@ class UserProfilesEventListener implements iEventServiceSetup $oCurrentUserProfileSet = $oUser->Get('profile_list'); if ($oCurrentUserProfileSet->Count() === 1){ $oProfile = $oCurrentUserProfileSet->Fetch(); + $sSingleProfileName = $oProfile->Get('profile'); - if (POWER_USER_PORTAL_PROFILE_NAME === $oProfile->Get('profile')){ - //add portal user - // power portal user is not a standalone profile (it will break console UI) - $oUserProfile = new \URP_UserProfile(); - $oUserProfile->Set('profileid', $this->oUserPortalProfile->GetKey()); - $oCurrentUserProfileSet->AddItem($oUserProfile); - $oUser->Set('profile_list', $oCurrentUserProfileSet); + if (array_key_exists($sSingleProfileName, $this->aNonStandaloneProfilesMap)) { + $sRepairingProfileId = $this->aNonStandaloneProfilesMap[$sSingleProfileName]; + if (is_null($sRepairingProfileId)){ + //notify current user via session messages that there will be an issue + //without preventing from commiting + } else { + //completing profiles profiles by adding repairing one : by default portal user to a power portal user + $oUserProfile = new \URP_UserProfile(); + $oUserProfile->Set('profileid', $sRepairingProfileId); + $oCurrentUserProfileSet->AddItem($oUserProfile); + $oUser->Set('profile_list', $oCurrentUserProfileSet); + } } } } diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php index 0be313ef1e..b88acd0e9a 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php @@ -377,6 +377,23 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); } + public function testUserProfilesEventListenerInit_badlyconfigured(){ + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, "a string instead of an array"); + + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + + $this->assertFalse($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testUserProfilesEventListenerInit_specifically_disabled(){ + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, []); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + + $this->assertFalse($oUserProfilesEventListener->IsRepairmentEnabled()); + } + public function CustomizedPortalsProvider(){ return [ 'console + customized portal' => [ @@ -411,7 +428,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase 'backoffice' ]; - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Portal user'); + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Portal user']); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init($aPortalDispatcherData); @@ -426,7 +443,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase 'backoffice' ]; - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Dummy Profile'); + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Dummy Profile']); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init($aPortalDispatcherData); @@ -434,6 +451,30 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $this->assertFalse($oUserProfilesEventListener->IsRepairmentEnabled()); } + public function testInit_ConfWithOneWarningProfile() { + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Change Supervisor' => 'Administrator', 'Portal power user' => null]); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testInit_ConfWithFurtherWarningProfiles() { + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Change Supervisor' => null, 'Portal power user' => null]); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + } + + public function testInit_ConfWithFurtherWarningProfilesAndOneRepairment() { + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Portal power user' => null, 'Change Supervisor' => null, 'Administrator' => "REST Services User"]); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + } + public function testRepairProfiles_WithAnotherFallbackProfile() { $oUser = new \UserLocal(); @@ -442,7 +483,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $oUser->Set('password', 'ABCD1234@gabuzomeu'); $oUser->Set('language', 'EN US'); - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', 'poweruserportal-repair-profile', 'Change Supervisor'); + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Change Supervisor']); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); @@ -459,4 +500,52 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $this->assertContains('Change Supervisor', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); $this->assertContains('Portal power user', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); } + + public function testRepairProfiles_MultiRepairmentConf() + { + \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + [ + 'Administrator' => 'REST Services User', + 'Portal power user' => 'Change Supervisor' + ] + ); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->Init(); + $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); + + $oUser = new \UserLocal(); + $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + $this->CreateUserForProfileTesting($oUser, ['Portal power user'], false); + $oUserProfilesEventListener->RepairProfiles($oUser); + + $oUserProfileList = $oUser->Get('profile_list'); + $aProfilesAfterCreation=[]; + while (($oProfile = $oUserProfileList->Fetch()) != null){ + $aProfilesAfterCreation[] = $oProfile->Get('profile'); + } + + $this->assertContains('Change Supervisor', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + $this->assertContains('Portal power user', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + + $oUser2 = new \UserLocal(); + $sLogin = 'testUserLocalCreationWithPortalPowerUserProfile-'.uniqid(); + $oUser2->Set('login', $sLogin); + $oUser2->Set('password', 'ABCD1234@gabuzomeu'); + $oUser2->Set('language', 'EN US'); + + $this->CreateUserForProfileTesting($oUser2, ['Administrator'], false); + $oUserProfilesEventListener->RepairProfiles($oUser2); + + $oUserProfileList = $oUser2->Get('profile_list'); + $aProfilesAfterCreation=[]; + while (($oProfile = $oUserProfileList->Fetch()) != null){ + $aProfilesAfterCreation[] = $oProfile->Get('profile'); + } + + $this->assertContains('Administrator', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + $this->assertContains('REST Services User', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); + } } From 97f4818076dc6424da32b21b85c664c95adf8539 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 26 Jun 2023 15:01:57 +0200 Subject: [PATCH 6/8] 5324- Guillaume PR feedbacks --- .../src/UserProfilesEventListener.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index 2a0cf0986b..a378836e33 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -92,8 +92,8 @@ class UserProfilesEventListener implements iEventServiceSetup $aNonStandaloneProfiles = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', self::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null); - //when there are customized portals on an itop, choosing a specific profile means choosing which portal user will access - //in that case, itop administrator has to specify it via itop configuration. we dont use default profiles repairment otherwise + //When there are several customized portals on an itop, choosing a specific profile means choosing which portal user will access + //In that case, itop administrator has to specify it via itop configuration. we dont use default profiles repairment otherwise if (is_null($aNonStandaloneProfiles)){ if (count($aPortalDispatcherData) > 2){ $this->bIsRepairmentEnabled = false; @@ -120,7 +120,7 @@ class UserProfilesEventListener implements iEventServiceSetup } if (empty($aNonStandaloneProfiles)){ - //feature specifically disabled in itop configuration + //Feature specifically disabled in itop configuration $this->bIsRepairmentEnabled = false; return; } @@ -176,7 +176,7 @@ class UserProfilesEventListener implements iEventServiceSetup } } - public function RepairProfiles(\User $oUser) : void + public function RepairProfiles(?\User $oUser) : void { if (!is_null($oUser)) { @@ -188,10 +188,10 @@ class UserProfilesEventListener implements iEventServiceSetup if (array_key_exists($sSingleProfileName, $this->aNonStandaloneProfilesMap)) { $sRepairingProfileId = $this->aNonStandaloneProfilesMap[$sSingleProfileName]; if (is_null($sRepairingProfileId)){ - //notify current user via session messages that there will be an issue - //without preventing from commiting + //Notify current user via session messages that there will be an issue + //Without preventing from commiting } else { - //completing profiles profiles by adding repairing one : by default portal user to a power portal user + //Completing profiles profiles by adding repairing one : by default portal user to a power portal user $oUserProfile = new \URP_UserProfile(); $oUserProfile->Set('profileid', $sRepairingProfileId); $oCurrentUserProfileSet->AddItem($oUserProfile); From 07eadb3ea7dffaf5ec42f0488a7eeeda24052814 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 28 Jun 2023 14:41:20 +0200 Subject: [PATCH 7/8] =?UTF-8?q?N=C2=B05324=20-rename=20and=20move=20conf?= =?UTF-8?q?=20parameter=20to=20security.single-profile-completion=20+=20di?= =?UTF-8?q?splay=20warningmessage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/config.class.inc.php | 8 ++ .../datamodel.itop-profiles-itil.xml | 20 +++- .../src/UserProfilesEventListener.php | 6 +- .../UserProfilesEventListenerTest.php | 105 ++++++++++++++---- 4 files changed, 115 insertions(+), 24 deletions(-) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 8d38d09be3..8443932cd2 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -1595,6 +1595,14 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ], + 'security.single-profile-completion' => [ + 'type' => 'array', + 'description' => 'Non standalone profiles can be completed by other profiles via this configuration. default configuration is equivalent to [\'Portal power user\' => \'Portal user\'] configuration. unless you have specific portal customization.', + 'default' => null, + 'value' => false, + 'source_of_value' => '', + 'show_in_conf_sample' => false, + ], 'behind_reverse_proxy' => [ 'type' => 'bool', 'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)', diff --git a/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml b/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml index d4e83b713f..2bdacbfdd0 100755 --- a/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml +++ b/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml @@ -1,5 +1,5 @@ - + @@ -544,5 +544,23 @@ + + + English + English + + User profile %1$s cannot be standalone. You should add + other prrofiles otherwise you may encounter access issue with this user. + + + + French + Français + + Le profil %1$s ne peut être seul. Sans rajout d'autres profiles, + l'utilisateur peut rencontrer des problèmes dans iTop. + + + diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index a378836e33..d47da74fa8 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -24,7 +24,7 @@ define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user'); */ class UserProfilesEventListener implements iEventServiceSetup { - const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'poweruserportal-repair-profile'; + const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'security.single-profile-completion'; private $bIsRepairmentEnabled = false; //map: non standalone profile name => repairing profile id @@ -90,7 +90,7 @@ class UserProfilesEventListener implements iEventServiceSetup $aPortalDispatcherData = \PortalDispatcherData::GetData(); } - $aNonStandaloneProfiles = \utils::GetConfig()->GetModuleSetting('itop-profiles-itil', self::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null); + $aNonStandaloneProfiles = \utils::GetConfig()->Get(self::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null); //When there are several customized portals on an itop, choosing a specific profile means choosing which portal user will access //In that case, itop administrator has to specify it via itop configuration. we dont use default profiles repairment otherwise @@ -190,6 +190,8 @@ class UserProfilesEventListener implements iEventServiceSetup if (is_null($sRepairingProfileId)){ //Notify current user via session messages that there will be an issue //Without preventing from commiting + $sMessage = \Dict::Format("Class:User/NonStandaloneProfileWarning", $sSingleProfileName); + $oUser::SetSessionMessage(get_class($oUser), $oUser->GetKey(), 1, $sMessage, 'WARNING', 1); } else { //Completing profiles profiles by adding repairing one : by default portal user to a power portal user $oUserProfile = new \URP_UserProfile(); diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php index b88acd0e9a..a1e1e7e6bc 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-profiles-itil/UserProfilesEventListenerTest.php @@ -26,14 +26,17 @@ namespace Combodo\iTop\Test\UnitTest\Module\iTopProfilesItil; +use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenuFactory; use Combodo\iTop\ItilProfiles\UserProfilesEventListener; +use Combodo\iTop\Service\Events\EventService; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use DBObjectSet; use URP_UserProfile; use UserRights; /** + * @since 3.1.0 N°5324 * @group itopRequestMgmt * @group userRights * @group defaultProfiles @@ -44,6 +47,13 @@ use UserRights; */ class UserProfilesEventListenerTest extends ItopDataTestCase { + public function setUp(): void { + parent::setUp(); + + //reset conf to have nominal behaviour + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null); + } + public function PortaPowerUserProvider(){ return [ 'Portal power user only => user should be repaired by adding User portal profile' => [ @@ -69,7 +79,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserLocalCreation($aAssociatedProfilesBeforeUserCreation, @@ -84,7 +93,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserLocalUpdate($aAssociatedProfilesBeforeUserCreation, @@ -99,7 +107,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserLDAPCreation($aAssociatedProfilesBeforeUserCreation, @@ -112,7 +119,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserLDAPUpdate($aAssociatedProfilesBeforeUserCreation, @@ -125,7 +131,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserExternalCreation($aAssociatedProfilesBeforeUserCreation, @@ -138,7 +143,6 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } /** - * @since 3.1.0 N°5324 * @dataProvider PortaPowerUserProvider */ public function testUserExternalUpdate($aAssociatedProfilesBeforeUserCreation, @@ -186,15 +190,15 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } public function commonUserCreation($oUserToCreate, $aAssociatedProfilesBeforeUserCreation, - $aExpectedAssociatedProfilesAfterUserCreation) + $aExpectedAssociatedProfilesAfterUserCreation, $bTestUserItopAccess=true) { $sUserClass = get_class($oUserToCreate); list ($sId, $aProfiles) = $this->CreateUserForProfileTesting($oUserToCreate, $aAssociatedProfilesBeforeUserCreation); - $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation); + $this->CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation, $bTestUserItopAccess); } - public function CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation){ + public function CheckProfilesAreOk($sUserClass, $sId, $aExpectedAssociatedProfilesAfterUserCreation, $bTestUserItopAccess=true){ $oUser = \MetaModel::GetObject($sUserClass, $sId); $oUserProfileList = $oUser->Get('profile_list'); $aProfilesAfterCreation=[]; @@ -207,6 +211,10 @@ class UserProfilesEventListenerTest extends ItopDataTestCase "profile \'$sExpectedProfileName\' should be asociated to user after creation. " . var_export($aProfilesAfterCreation, true) ); } + if (! $bTestUserItopAccess){ + return; + } + $_SESSION = []; //$this->expectException(\Exception::class); @@ -378,7 +386,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } public function testUserProfilesEventListenerInit_badlyconfigured(){ - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, "a string instead of an array"); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, "a string instead of an array"); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); @@ -387,7 +395,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } public function testUserProfilesEventListenerInit_specifically_disabled(){ - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, []); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, []); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); @@ -428,7 +436,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase 'backoffice' ]; - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Portal user']); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Portal user']); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init($aPortalDispatcherData); @@ -443,7 +451,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase 'backoffice' ]; - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Dummy Profile']); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Dummy Profile']); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init($aPortalDispatcherData); @@ -452,24 +460,27 @@ class UserProfilesEventListenerTest extends ItopDataTestCase } public function testInit_ConfWithOneWarningProfile() { - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, - ['Change Supervisor' => 'Administrator', 'Portal power user' => null]); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Change Supervisor' => 'Administrator', 'Portal power user' => null] + ); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); } public function testInit_ConfWithFurtherWarningProfiles() { - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, - ['Change Supervisor' => null, 'Portal power user' => null]); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Change Supervisor' => null, 'Portal power user' => null] + ); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); } public function testInit_ConfWithFurtherWarningProfilesAndOneRepairment() { - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, - ['Portal power user' => null, 'Change Supervisor' => null, 'Administrator' => "REST Services User"]); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Portal power user' => null, 'Change Supervisor' => null, 'Administrator' => "REST Services User"] + ); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); @@ -483,7 +494,9 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $oUser->Set('password', 'ABCD1234@gabuzomeu'); $oUser->Set('language', 'EN US'); - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, ['Portal power user' => 'Change Supervisor']); + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Portal power user' => 'Change Supervisor'] + ); $oUserProfilesEventListener = new UserProfilesEventListener(); $oUserProfilesEventListener->Init(); $this->assertTrue($oUserProfilesEventListener->IsRepairmentEnabled()); @@ -503,7 +516,7 @@ class UserProfilesEventListenerTest extends ItopDataTestCase public function testRepairProfiles_MultiRepairmentConf() { - \MetaModel::GetConfig()->SetModuleSetting('itop-profiles-itil', UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, [ 'Administrator' => 'REST Services User', 'Portal power user' => 'Change Supervisor' @@ -548,4 +561,54 @@ class UserProfilesEventListenerTest extends ItopDataTestCase $this->assertContains('Administrator', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); $this->assertContains('REST Services User', $aProfilesAfterCreation, var_export($aProfilesAfterCreation, true)); } + + public function testUserCreationWithWarningMessageConf() + { + $_SESSION = []; + $oAdminUser = new \UserLocal(); + $sLogin = 'testUserCreationWithWarningMessageConf-Admin'.uniqid(); + $oAdminUser->Set('login', $sLogin); + $oAdminUser->Set('password', 'ABCD1234@gabuzomeu'); + $oAdminUser->Set('language', 'EN US'); + $aAssociatedProfilesBeforeUserCreation = ['Administrator']; + $this->commonUserCreation($oAdminUser, $aAssociatedProfilesBeforeUserCreation, $aAssociatedProfilesBeforeUserCreation, false); + UserRights::Login($oAdminUser->Get('login')); + + + + $aAssociatedProfilesBeforeUserCreation = [ + 'Portal power user' + ]; + + $oUser = new \UserLocal(); + $sLogin = 'testUserCreationWithWarningMessageConf-'.uniqid(); + $oUser->Set('login', $sLogin); + $oUser->Set('password', 'ABCD1234@gabuzomeu'); + $oUser->Set('language', 'EN US'); + + \MetaModel::GetConfig()->Set(UserProfilesEventListener::USERPROFILE_REPAIR_ITOP_PARAM_NAME, + ['Portal power user' => null ] + ); + + $this->SetNonPublicStaticProperty(EventService::class, "aEventListeners", []); + $oUserProfilesEventListener = new UserProfilesEventListener(); + $oUserProfilesEventListener->RegisterEventsAndListeners(); + + $this->commonUserCreation($oUser, $aAssociatedProfilesBeforeUserCreation, $aAssociatedProfilesBeforeUserCreation, false); + $aObjMessages = Session::Get('obj_messages'); + $this->assertNotEmpty($aObjMessages); + $sKey = sprintf("%s::%s", get_class($oUser), $oUser->GetKey()); + $this->assertTrue(array_key_exists($sKey, $aObjMessages)); + + $aExpectedMessages = [ + [ + 'rank' => 1, + 'severity' => 'WARNING', + 'message' => \Dict::Format("Class:User/NonStandaloneProfileWarning", 'Portal power user') + ] + ]; + $this->assertEquals($aExpectedMessages, array_values($aObjMessages[$sKey]), var_export($aObjMessages[$sKey], true)); + + $_SESSION = []; + } } From 427fc6f9f9855863e62c312ce922ae487314c034 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 28 Jun 2023 21:31:11 +0200 Subject: [PATCH 8/8] 5324-Guillaume s feedback in PR --- core/config.class.inc.php | 2 +- .../datamodel.itop-profiles-itil.xml | 16 ++++++---------- .../src/UserProfilesEventListener.php | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 8443932cd2..a7c64579f7 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -1595,7 +1595,7 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ], - 'security.single-profile-completion' => [ + 'security.single_profile_completion' => [ 'type' => 'array', 'description' => 'Non standalone profiles can be completed by other profiles via this configuration. default configuration is equivalent to [\'Portal power user\' => \'Portal user\'] configuration. unless you have specific portal customization.', 'default' => null, diff --git a/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml b/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml index 2bdacbfdd0..c0e45d451c 100755 --- a/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml +++ b/datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml @@ -545,20 +545,16 @@ - - English - English + - User profile %1$s cannot be standalone. You should add - other prrofiles otherwise you may encounter access issue with this user. + User profile %1$s cannot be standalone. You should add + other profiles otherwise you may encounter access issue with this user. - - French - Français + - Le profil %1$s ne peut être seul. Sans rajout d'autres profiles, - l'utilisateur peut rencontrer des problèmes dans iTop. + Le profil %1$s ne peut être seul. Sans le rajout d'autres + profiles, l'utilisateur peut rencontrer des problèmes dans iTop. diff --git a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php index d47da74fa8..1da291fdc9 100644 --- a/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php +++ b/datamodels/2.x/itop-profiles-itil/src/UserProfilesEventListener.php @@ -24,7 +24,7 @@ define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user'); */ class UserProfilesEventListener implements iEventServiceSetup { - const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'security.single-profile-completion'; + const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'security.single_profile_completion'; private $bIsRepairmentEnabled = false; //map: non standalone profile name => repairing profile id