Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 53 additions & 29 deletions addons/userrights/userrightsprofile.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -582,16 +582,26 @@ public function IsPortalUser($oUser)
*/
public function ListProfiles($oUser)
{
$aRet = [];
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oSearch->NoContextParameters();
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
$oProfiles = new DBObjectSet($oSearch);
while ($oUserProfile = $oProfiles->Fetch()) {
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
if (count($oUser->ListChanges()) === 0) { // backward compatibility
Comment thread
jf-cbd marked this conversation as resolved.
$aRet = [];
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oSearch->NoContextParameters();
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
$oProfiles = new DBObjectSet($oSearch);
while ($oUserProfile = $oProfiles->Fetch()) {
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
}

return $aRet;
} else {
Comment thread
jf-cbd marked this conversation as resolved.
$aRet = [];
$oProfilesSet = $oUser->Get('profile_list');
foreach ($oProfilesSet as $oUserProfile) {
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
}
return $aRet;
}
return $aRet;
}

public function GetSelectFilter($oUser, $sClass, $aSettings = [])
Expand Down Expand Up @@ -705,26 +715,23 @@ public function GetProfileActionGrant($iProfile, $sClass, $sAction)
protected function GetUserActionGrant($oUser, $sClass, $iActionCode)
{
$this->LoadCache();

// load and cache permissions for the current user on the given class
//
$iUser = $oUser->GetKey();
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])) {
$aTest = $this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
if (is_array($aTest)) {
return $aTest;
if (count($oUser->ListChanges()) === 0) {
// load and cache permissions for the current user on the given class
if (isset($this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode])) {
$aTest = $this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode];
if (is_array($aTest)) {
return $aTest;
}
}
}

$sAction = self::$m_aActionCodes[$iActionCode];

$bStatus = null;
// Cache user's profiles
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}

$aProfileList = $this->GetProfileList($oUser);
// Call the API of UserRights because it caches the list for us
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
foreach ($aProfileList as $iProfile => $oProfile) {
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant)) {
if ($bGrant) {
Expand All @@ -742,7 +749,9 @@ protected function GetUserActionGrant($oUser, $sClass, $iActionCode)
$aRes = [
'permission' => $iPermission,
];
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
if (count($oUser->ListChanges()) === 0) {
$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode] = $aRes;
}
return $aRes;
}

Expand Down Expand Up @@ -824,18 +833,14 @@ public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet
{
$this->LoadCache();
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();

// Cache user's profiles
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
$aProfileList = $this->GetProfileList($oUser);

// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
foreach ($aProfileList as $iProfile => $oProfile) {
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant)) {
if ($bGrant) {
Expand Down Expand Up @@ -893,6 +898,25 @@ protected static function HasSharing()
}
return $bHasSharing;
}

/**
* @param \User $oUser
*
* @return array
* @throws \Exception
*/
public function GetProfileList(User $oUser): array
{
if (count($oUser->ListChanges()) === 0) { // if user is already in db and not changed
$iUser = $oUser->GetKey();
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
$aProfiles = UserRights::ListProfiles($oUser);
$this->aUsersProfilesList[$iUser] = $aProfiles;
}
return $this->aUsersProfilesList[$iUser];
}
return UserRights::ListProfiles($oUser);
}
}

UserRights::SelectModule('UserRightsProfile');
11 changes: 11 additions & 0 deletions tests/php-unit-tests/unitary-tests/core/UserRightsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ protected function CreateUniqueUserAndLogin(string $sLoginPrefix, int $iProfileI
return $oUser;
}

public function testIsActionAllowedWithNonInstantiatedUserObject()
{
$oUser = $this->GivenUserWithProfiles('test1', [self::$aURP_Profiles['Configuration Manager']]); // not a readonly profile
$oAdminUser = $this->GivenUserWithProfiles('test2', [self::$aURP_Profiles['Administrator']]);
$oAdminUser->DBInsert();
$_SESSION = [];
UserRights::Login($oAdminUser->Get('login'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason why you have to log in in this test?

@jf-cbd jf-cbd Jun 24, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because IsActionAllowed method starts with

		if (!self::CheckLogin()) {
			return UR_ALLOWED_YES;
		}

So without login, the test isn't relevant


self::assertTrue(UserRights::IsActionAllowed('Server', UR_ACTION_MODIFY, null, $oUser) === UR_ALLOWED_YES);
Comment thread
odain-cbd marked this conversation as resolved.
}

/**
* @param array $aProfileIds
* @param array $aShouldBeAllowedToSeeClass
Expand Down