mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-21 08:12:26 +02:00
Merge branch 'feature/5324-powerportaluser-repairprofiles' into saas/3.1.0
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design version="3.1">
|
||||
<classes/>
|
||||
<user_rights>
|
||||
<groups>
|
||||
@@ -544,5 +544,19 @@
|
||||
<groups/>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dictionaries>
|
||||
<dictionary id="EN US">
|
||||
<entries>
|
||||
<entry id="Class:User/NonStandaloneProfileWarning" _delta="define">User profile %1$s cannot be standalone. You should add
|
||||
other profiles otherwise you may encounter access issue with this user.</entry>
|
||||
</entries>
|
||||
</dictionary>
|
||||
<dictionary id="FR FR">
|
||||
<entries>
|
||||
<entry id="Class:User/NonStandaloneProfileWarning" _delta="define">Le profil %1$s ne peut être seul. Sans le rajout d'autres
|
||||
profiles, l'utilisateur peut rencontrer des problèmes dans iTop.</entry>
|
||||
</entries>
|
||||
</dictionary>
|
||||
</dictionaries>
|
||||
</user_rights>
|
||||
</itop_design>
|
||||
|
||||
@@ -36,6 +36,7 @@ SetupWebPage::AddModule(
|
||||
// Components
|
||||
//
|
||||
'datamodel' => array(
|
||||
'src/UserProfilesEventListener.php'
|
||||
),
|
||||
'webservice' => array(
|
||||
//'webservices.itop-profiles-itil.php',
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\ItilProfiles;
|
||||
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use LogChannels;
|
||||
|
||||
define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user');
|
||||
|
||||
/**
|
||||
* Class UserProfilesEventListener
|
||||
*
|
||||
* @package Combodo\iTop\Core\EventListener
|
||||
* @since 3.1 N°5324 - Avoid to have users with non-standalone power portal profile only
|
||||
*
|
||||
*/
|
||||
class UserProfilesEventListener implements iEventServiceSetup
|
||||
{
|
||||
const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'security.single_profile_completion';
|
||||
private $bIsRepairmentEnabled = false;
|
||||
|
||||
//map: non standalone profile name => repairing profile id
|
||||
private $aNonStandaloneProfilesMap = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function RegisterEventsAndListeners()
|
||||
{
|
||||
$this->Init();
|
||||
|
||||
if (false === $this->bIsRepairmentEnabled){
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = [$this, 'OnUserProfileLinkChange'];
|
||||
$aEventSource = [\User::class, \UserExternal::class, \UserInternal::class];
|
||||
|
||||
EventService::RegisterListener(
|
||||
EVENT_DB_BEFORE_WRITE,
|
||||
$callback,
|
||||
$aEventSource
|
||||
);
|
||||
|
||||
EventService::RegisterListener(
|
||||
EVENT_DB_LINKS_CHANGED,
|
||||
$callback,
|
||||
$aEventSource
|
||||
);
|
||||
}
|
||||
|
||||
public function IsRepairmentEnabled() : bool
|
||||
{
|
||||
return $this->bIsRepairmentEnabled;
|
||||
}
|
||||
|
||||
public function OnUserProfileLinkChange(EventData $oEventData): void {
|
||||
/** @var \User $oObject */
|
||||
$oUser = $oEventData->Get('object');
|
||||
|
||||
try {
|
||||
$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' => $e->getMessage(),
|
||||
'exception_stacktrace' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
$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
|
||||
if (is_null($aNonStandaloneProfiles)){
|
||||
if (count($aPortalDispatcherData) > 2){
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$aPortalNames = array_keys($aPortalDispatcherData);
|
||||
sort($aPortalNames);
|
||||
if ($aPortalNames !== ['backoffice', 'itop-portal']){
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
$this->FetchRepairingProfileIds($aNonStandaloneProfiles);
|
||||
} 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 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))
|
||||
{
|
||||
$oCurrentUserProfileSet = $oUser->Get('profile_list');
|
||||
if ($oCurrentUserProfileSet->Count() === 1){
|
||||
$oProfile = $oCurrentUserProfileSet->Fetch();
|
||||
$sSingleProfileName = $oProfile->Get('profile');
|
||||
|
||||
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
|
||||
$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();
|
||||
$oUserProfile->Set('profileid', $sRepairingProfileId);
|
||||
$oCurrentUserProfileSet->AddItem($oUserProfile);
|
||||
$oUser->Set('profile_list', $oCurrentUserProfileSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user