N°4354 - Hide Administrator profile to non-admins

This commit is contained in:
denis.flaven@combodo.com
2021-10-06 15:34:17 +02:00
parent 9fbc631b07
commit bf4835eec0
7 changed files with 266 additions and 21 deletions

View File

@@ -34,7 +34,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -219,7 +219,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => array("userlogin", "profile"),
"state_attcode" => "",
@@ -610,30 +610,112 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
$this->LoadCache();
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
if (!static::IsAdministrator($oUser)) // Let us pass an administrator for testing without the need of setting up complex profile
{
return false;
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
{
return false;
}
}
// Determine how to position the objects of this class
//
$oFilter = true;
$aConditions = array();
// Determine if this class is part of a silo and build the filter for it
$sAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (is_null($sAttCode))
if (!is_null($sAttCode))
{
// No filtering for this object
return true;
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (count($aUserOrgs) > 0)
{
$oFilter = $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
}
// else: No org means 'any org'
}
// Position the user
//
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (count($aUserOrgs) == 0)
// else: No silo for this class
// Specific conditions to hide, for non-administrators, the Administrator Users, the Administrator Profile and related links
// Note: when logged as an administrator, GetSelectFilter is completely bypassed.
if ($this->AdministratorsAreHidden())
{
// No org means 'any org'
return true;
if ($sClass == 'URP_Profiles')
{
$oExpression = new FieldExpression('id', $sClass);
$oScalarExpr = new ScalarExpression(1);
$aConditions[] = new BinaryExpression($oExpression, '!=', $oScalarExpr);
}
else if (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User')))
{
$aAdministrators = $this->GetAdministrators();
if (count($aAdministrators) > 0)
{
$sAttCode = ($sClass == 'URP_UserProfile') ? 'userid' : 'id';
$oExpression = new FieldExpression($sAttCode, $sClass);
$oListExpr = ListExpression::FromScalars($aAdministrators);
$aConditions[] = new BinaryExpression($oExpression, 'NOT IN', $oListExpr);
}
}
}
return $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
// Handling of the added conditions
if (count($aConditions) > 0)
{
if($oFilter === true)
{
// No 'silo' filter, let's build a clean one
$oFilter = new DBObjectSearch($sClass);
}
// Add the conditions to the filter
foreach($aConditions as $oCondition)
{
$oFilter->AddConditionExpression($oCondition);
}
}
return $oFilter;
}
/**
* Retrieve (and memoize) the list of administrator accounts.
* Note that there should always be at least one administrator account
* @return number[]
*/
private function GetAdministrators()
{
static $aAdministrators = null;
if ($aAdministrators === null)
{
// Find all administrators
$aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
$oScalarExpr = new ScalarExpression(1);
$oCondition = new BinaryExpression($oExpression, '=', $oScalarExpr);
$oLnkFilter->AddConditionExpression($oCondition);
$oAdministratorsFilter->AddCondition_ReferencedBy($oLnkFilter, 'userid');
$oAdministratorsFilter->AllowAllData(true); // Mandatory to prevent infinite recursion !!
$oSet = new DBObjectSet($oAdministratorsFilter);
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$aAdministrators[] = $oUser->GetKey();
}
}
return $aAdministrators;
}
/**
* Whether or not to hide the 'Administrator' profile and the administrator accounts
* @return boolean
*/
private function AdministratorsAreHidden()
{
return ((bool)MetaModel::GetConfig()->Get('security.hide_administrators'));
}

View File

@@ -1503,6 +1503,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.hide_administrators' => [
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
];
public function IsProperty($sPropCode)

View File

@@ -638,7 +638,7 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core,grant_by_profile",
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -37,7 +37,7 @@ class UserExternal extends User
{
$aParams = array
(
"category" => "addon/authentication,grant_by_profile",
"category" => "addon/authentication,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -14,7 +14,7 @@
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/</comment>
<category>addon/authentication,grant_by_profile</category>
<category>addon/authentication,grant_by_profile,silo</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_user_ldap</db_table>

View File

@@ -77,7 +77,7 @@ class UserLocal extends UserInternal
{
$aParams = array
(
"category" => "addon/authentication,grant_by_profile",
"category" => "addon/authentication,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -0,0 +1,155 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Webservices;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use DBObjectSearch;
use DBObjectSet;
use Exception;
use MetaModel;
use UserRightsProfile;
use utils;
class GetSelectFilterTest extends ItopDataTestCase
{
private $sLogin;
private $sPassword = "IAAuytrez9876[}543ç_è-(";
private $oUser;
protected function setUp()
{
parent::setUp();
require_once(APPROOT.'application/startup.inc.php');
$oRestProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'REST Services User'), true);
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true);
$this->sLogin = "getselectfilter-user-" . date('dmYHis');
// Ensure that we have at least one administrator account
if (is_object($oRestProfile) && is_object($oAdminProfile))
{
$this->oUser = $this->CreateUser($this->sLogin, $oRestProfile->GetKey(), $this->sPassword);
$this->AddProfileToUser($this->oUser, $oAdminProfile->GetKey());
}
}
public function testGetSelectFilter()
{
$oUserRights = new UserRightsProfile();
$aClasses = get_declared_classes();
$aUserClasses = ['User'];
$aUserLocalAncestors = ['User', 'UserInternal', 'UserLocal'];
foreach($aClasses as $sClass)
{
if (is_subclass_of($sClass, 'User'))
{
$aUserClasses[] = $sClass;
}
}
$oConfig = MetaModel::GetConfig();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default behavior: Administrators, Administrator profile and URP_UserProfile related to administrators are visible
// via GetSelectFilter
$oConfig->Set('security.hide_administrators', false);
$oFilterProfiles = $oUserRights->GetSelectFilter($this->oUser, 'URP_Profiles');
if ($oFilterProfiles === true)
{
$oFilterProfiles = new DBObjectSearch('URP_Profiles');
}
$oSet = new DBObjectSet($oFilterProfiles);
$bAdminProfileFound = false;
while($oProfile = $oSet->Fetch())
{
if ($oProfile->GetKey() == 1)
{
$bAdminProfileFound = true;
break;
}
}
$this->assertEquals($bAdminProfileFound, true);
foreach($aUserLocalAncestors as $sUserClass)
{
$bAdminUserFound = false;
$oFilterUser = $oUserRights->GetSelectFilter($this->oUser,$sUserClass);
if ($oFilterUser === true)
{
$oFilterUser = new DBObjectSearch($sUserClass);
}
$oSet = new DBObjectSet($oFilterUser);
while($oUser = $oSet->Fetch())
{
if($oUser->GetKey() == $this->oUser->GetKey())
{
$bAdminUserFound = true;
break;
}
}
$this->assertEquals($bAdminUserFound, true);
}
$oFilterLnkProfiles = $oUserRights->GetSelectFilter($this->oUser, 'URP_UserProfile');
if ($oFilterLnkProfiles === true)
{
$oFilterLnkProfiles = new DBObjectSearch('URP_UserProfile');
}
$oSet = new DBObjectSet($oFilterLnkProfiles);
// There should some lnk referencing either our administrator account or the Administrator profile
$bUserFound = false;
$bProfileFound = false;
while($oLnk = $oSet->Fetch())
{
if($oLnk->Get('userid') == $this->oUser->GetKey())
{
$bUserFound = true;
}
if($oLnk->Get('profileid') == 1)
{
$bProfileFound = true;
}
}
$this->assertEquals($bUserFound, true);
$this->assertEquals($bProfileFound, true);
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Administrator account, Administrator profile and URP_UserProfile related to administrators are now hidden
// via GetSelectFilter
$oConfig->Set('security.hide_administrators', true);
$oFilterProfiles = $oUserRights->GetSelectFilter($this->oUser, 'URP_Profiles');
$this->assertNotEquals($oFilterProfiles,true); // This class must be filtered
$oSet = new DBObjectSet($oFilterProfiles);
while($oProfile = $oSet->Fetch())
{
$this->assertNotEquals($oProfile->GetKey(), 1); // No profile should have id = 1 (Administrator)
}
foreach($aUserClasses as $sUserClass)
{
$oFilterUser = $oUserRights->GetSelectFilter($this->oUser, $sUserClass);
$this->assertNotEquals($oFilterUser,true); // This class must be filtered
$oSet = new DBObjectSet($oFilterUser);
while($oUser = $oSet->Fetch())
{
$this->assertNotEquals($oUser->GetKey(), $this->oUser->GetKey()); // Our administrator account should not be visible
}
}
$oFilterLnkProfiles = $oUserRights->GetSelectFilter($this->oUser, 'URP_UserProfile');
$this->assertNotEquals($oFilterLnkProfiles,true); // This class must be filtered
$oSet = new DBObjectSet($oFilterLnkProfiles);
// There should be no lnk referencing either our administrator account or the profile Administrator
while($oLnk = $oSet->Fetch())
{
$this->assertNotEquals($oLnk->Get('userid'), $this->oUser->GetKey());
$this->assertNotEquals($oLnk->Get('profileid'), 1);
}
}
}