// /** * Created by PhpStorm. * User: Eric * Date: 25/01/2018 * Time: 11:12 */ namespace Combodo\iTop\Test\UnitTest\Core; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use CoreCannotSaveObjectException; use CoreException; use DBObject; use DBObjectSearch; use DBObjectSet; use DeleteException; use URP_UserProfile; use UserRights; use utils; /** * @group itopRequestMgmt * @group userRights * @group defaultProfiles */ class UserRightsTest extends ItopDataTestCase { public function setUp(): void { parent::setUp(); try { utils::GetConfig()->SetModuleSetting('authent-local', 'password_validation.pattern', ''); self::CreateUser('admin', 1); } catch (CoreCannotSaveObjectException $e) { } } public static $aClasses = [ 'FunctionalCI' => ['class' => 'FunctionalCI', 'attcode' => 'name'], 'URP_UserProfile' => ['class' => 'URP_UserProfile', 'attcode' => 'reason'], 'UserLocal' => ['class' => 'UserLocal', 'attcode' => 'login'], 'UserRequest' => ['class' => 'UserRequest', 'attcode' => 'title'], 'ModuleInstallation' => ['class' => 'ModuleInstallation', 'attcode' => 'name'], ]; /** * @param string $sLoginPrefix * @param int $iProfileId initial profile * * @return \DBObject * @throws \CoreException * @throws \Exception */ protected function CreateUniqueUserAndLogin(string $sLoginPrefix, int $iProfileId): DBObject { static $iCount = 0; $sLogin = $sLoginPrefix.$iCount; $iCount++; $oUser = self::CreateUser($sLogin, $iProfileId); $_SESSION = array(); UserRights::Login($sLogin); return $oUser; } public function testIsLoggedIn() { $this->assertFalse(UserRights::IsLoggedIn()); } /** * Test Login validation * * @dataProvider LoginProvider * * @param $sLogin * @param $bResult * * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testLogin($sLogin, $bResult) { $_SESSION = []; $this->assertEquals($bResult, UserRights::Login($sLogin)); $this->assertEquals($bResult, UserRights::IsLoggedIn()); UserRights::Logoff(); } public function LoginProvider(): array { return [ ['admin', true], ['NotALoginForUnitTests', false], ['', false], ]; } /** Test IsActionAllowed when not logged => always true * * @dataProvider ActionAllowedNotLoggedProvider * * @param $aClassAction * * @throws \CoreException */ public function testIsActionAllowedNotLogged($aClassAction) { $bRes = UserRights::IsActionAllowed($aClassAction['class'], $aClassAction['action']) == UR_ALLOWED_YES; $this->assertEquals(true, $bRes); } public function ActionAllowedNotLoggedProvider(): array { $aClassActions = []; foreach (array_keys(self::$aClasses) as $sClass) { for ($i = 1; $i < 8; $i++) { $aClassAction = ['class' => $sClass, 'action' => $i]; $aClassActions[] = [$aClassAction]; } } return $aClassActions; } /** * @dataProvider ActionAllowedProvider * * @param int $iProfileId * @param array $aClassActionResult * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testIsActionAllowed(int $iProfileId, array $aClassActionResult) { $this->CreateUniqueUserAndLogin('test1', $iProfileId); $bRes = UserRights::IsActionAllowed($aClassActionResult['class'], $aClassActionResult['action']) == UR_ALLOWED_YES; $this->assertEquals($aClassActionResult['res'], $bRes); UserRights::Logoff(); } /* * FunctionalCI => bizmodel searchable * UserRequest => bizmodel searchable requestmgmt * URP_UserProfile => addon/userrights * UserLocal => addon/authentication * ModuleInstallation => core view_in_gui * * Profiles: * 1 - Administrator * 2 - User Portal * 3 - Configuration manager * */ public function ActionAllowedProvider(): array { return [ /* Administrator (7 = UR_ACTION_CREATE) */ 'Administrator FunctionalCI write' => [1, ['class' => 'FunctionalCI', 'action' => 7, 'res' => true]], 'Administrator UserRequest write' => [1, ['class' => 'UserRequest', 'action' => 7, 'res' => true]], 'Administrator URP_UserProfile write' => [1, ['class' => 'URP_UserProfile', 'action' => 7, 'res' => true]], 'Administrator UserLocal write' => [1, ['class' => 'UserLocal', 'action' => 7, 'res' => true]], 'Administrator ModuleInstallation write' => [1, ['class' => 'ModuleInstallation', 'action' => 7, 'res' => true]], /* User Portal (7 = UR_ACTION_CREATE) */ 'User Portal FunctionalCI write' => [2, ['class' => 'FunctionalCI', 'action' => 7, 'res' => false]], 'User Portal UserRequest write' => [2, ['class' => 'UserRequest', 'action' => 7, 'res' => true]], 'User Portal URP_UserProfile write' => [2, ['class' => 'URP_UserProfile', 'action' => 7, 'res' => false]], 'User Portal UserLocal write' => [2, ['class' => 'UserLocal', 'action' => 7, 'res' => false]], 'User Portal ModuleInstallation write' => [2, ['class' => 'ModuleInstallation', 'action' => 7, 'res' => false]], /* Configuration manager (7 = UR_ACTION_CREATE) */ 'Configuration manager FunctionalCI write' => [3, ['class' => 'FunctionalCI', 'action' => 7, 'res' => true]], 'Configuration manager UserRequest write' => [3, ['class' => 'UserRequest', 'action' => 7, 'res' => false]], 'Configuration manager URP_UserProfile write' => [3, ['class' => 'URP_UserProfile', 'action' => 7, 'res' => false]], 'Configuration manager UserLocal write' => [3, ['class' => 'UserLocal', 'action' => 7, 'res' => false]], 'Configuration manager ModuleInstallation write' => [3, ['class' => 'ModuleInstallation', 'action' => 7, 'res' => false]], /* Administrator (1 = UR_ACTION_READ) */ 'Administrator FunctionalCI read' => [1, ['class' => 'FunctionalCI', 'action' => 1, 'res' => true]], 'Administrator UserRequest read' => [1, ['class' => 'UserRequest', 'action' => 1, 'res' => true]], 'Administrator URP_UserProfile read' => [1, ['class' => 'URP_UserProfile', 'action' => 1, 'res' => true]], 'Administrator UserLocal read' => [1, ['class' => 'UserLocal', 'action' => 1, 'res' => true]], 'Administrator ModuleInstallation read' => [1, ['class' => 'ModuleInstallation', 'action' => 1, 'res' => true]], /* User Portal (1 = UR_ACTION_READ) */ 'User Portal FunctionalCI read' => [2, ['class' => 'FunctionalCI', 'action' => 1, 'res' => true]], 'User Portal UserRequest read' => [2, ['class' => 'UserRequest', 'action' => 1, 'res' => true]], 'User Portal URP_UserProfile read' => [2, ['class' => 'URP_UserProfile', 'action' => 1, 'res' => false]], 'User Portal UserLocal read' => [2, ['class' => 'UserLocal', 'action' => 1, 'res' => false]], 'User Portal ModuleInstallation read' => [2, ['class' => 'ModuleInstallation', 'action' => 1, 'res' => true]], /* Configuration manager (1 = UR_ACTION_READ) */ 'Configuration manager FunctionalCI read' => [3, ['class' => 'FunctionalCI', 'action' => 1, 'res' => true]], 'Configuration manager UserRequest read' => [3, ['class' => 'UserRequest', 'action' => 1, 'res' => true]], 'Configuration manager URP_UserProfile read' => [3, ['class' => 'URP_UserProfile', 'action' => 1, 'res' => false]], 'Configuration manager UserLocal read' => [3, ['class' => 'UserLocal', 'action' => 1, 'res' => false]], 'Configuration manager ModuleInstallation read' => [3, ['class' => 'ModuleInstallation', 'action' => 1, 'res' => true]], ]; } /** Test IsActionAllowedOnAttribute * * @dataProvider ActionAllowedOnAttributeProvider * * @param int $iProfileId * @param array $aClassActionResult * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testIsActionAllowedOnAttribute(int $iProfileId, array $aClassActionResult) { $this->CreateUniqueUserAndLogin('test1', $iProfileId); $sClass = $aClassActionResult['class']; $bRes = UserRights::IsActionAllowedOnAttribute($sClass, self::$aClasses[$sClass]['attcode'], $aClassActionResult['action']) == UR_ALLOWED_YES; $this->assertEquals($aClassActionResult['res'], $bRes); UserRights::Logoff(); } /* * FunctionalCI => bizmodel searchable * UserRequest => bizmodel searchable requestmgmt * URP_UserProfile => addon/userrights grant_by_profile * UserLocal => addon/authentication grant_by_profile * ModuleInstallation => core view_in_gui * */ public function ActionAllowedOnAttributeProvider(): array { return [ /* Administrator (2 = UR_ACTION_MODIFY) */ 'Administrator FunctionalCI' => [1, ['class' => 'FunctionalCI', 'action' => 2, 'res' => true]], 'Administrator UserRequest' => [1, ['class' => 'UserRequest', 'action' => 2, 'res' => true]], 'Administrator URP_UserProfile' => [1, ['class' => 'URP_UserProfile', 'action' => 2, 'res' => true]], 'Administrator UserLocal' => [1, ['class' => 'UserLocal', 'action' => 2, 'res' => true]], 'Administrator ModuleInstallation' => [1, ['class' => 'ModuleInstallation', 'action' => 2, 'res' => true]], /* User Portal (2 = UR_ACTION_MODIFY) */ 'User Portal FunctionalCI' => [2, ['class' => 'FunctionalCI', 'action' => 2, 'res' => false]], 'User Portal UserRequest' => [2, ['class' => 'UserRequest', 'action' => 2, 'res' => true]], 'User Portal URP_UserProfile' => [2, ['class' => 'URP_UserProfile', 'action' => 2, 'res' => false]], 'User Portal UserLocal' => [2, ['class' => 'UserLocal', 'action' => 2, 'res' => false]], 'User Portal ModuleInstallation' => [2, ['class' => 'ModuleInstallation', 'action' => 2, 'res' => true]], /* Configuration manager (2 = UR_ACTION_MODIFY) */ 'Configuration manager FunctionalCI' => [3, ['class' => 'FunctionalCI', 'action' => 2, 'res' => true]], 'Configuration manager UserRequest' => [3, ['class' => 'UserRequest', 'action' => 2, 'res' => false]], 'Configuration manager URP_UserProfile' => [3, ['class' => 'URP_UserProfile', 'action' => 2, 'res' => false]], 'Configuration manager UserLocal' => [3, ['class' => 'UserLocal', 'action' => 2, 'res' => false]], 'Configuration manager ModuleInstallation' => [3, ['class' => 'ModuleInstallation', 'action' => 2, 'res' => true]], ]; } /** * @dataProvider ProfileDenyingConsoleProvider * @doesNotPerformAssertions * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testProfileDenyingConsole(int $iProfileId) { $oUser = $this->CreateUniqueUserAndLogin('test1', $iProfileId); try { $this->AddProfileToUser($oUser, 2); $this->fail('Profile should not be added'); } catch (CoreCannotSaveObjectException $e) { } // logout $_SESSION = []; UserRights::Logoff(); } public function ProfileDenyingConsoleProvider(): array { return [ 'Administrator' => [1], ]; } /** * @dataProvider ProfileCannotModifySelfProvider * @doesNotPerformAssertions * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testProfileCannotModifySelf(int $iProfileId) { $oUser = $this->CreateUniqueUserAndLogin('test1', $iProfileId); try { $this->AddProfileToUser($oUser, 1); // trying to become an admin $this->fail('User should not modify self'); } catch (CoreException $e) { } // logout $_SESSION = []; UserRights::Logoff(); } public function ProfileCannotModifySelfProvider(): array { return [ 'Configuration manager' => [3], ]; } /** * @dataProvider DeletingSelfUserProvider * @doesNotPerformAssertions * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testDeletingSelfUser(int $iProfileId) { $oUser = $this->CreateUniqueUserAndLogin('test1', $iProfileId); try { $oUser->DBDelete(); $this->fail('Current User cannot be deleted'); } catch (DeleteException $e) { } // logout $_SESSION = []; UserRights::Logoff(); } public function DeletingSelfUserProvider(): array { return [ 'Administrator' => [1], 'Configuration manager' => [3], ]; } /** * @dataProvider RemovingOwnContactProvider * @doesNotPerformAssertions * * @param int $iProfileId * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testRemovingOwnContact(int $iProfileId) { $oUser = $this->CreateUniqueUserAndLogin('test1', $iProfileId); $oUser->Set('contactid', 0); try { $oUser->DBWrite(); $this->fail('Current User cannot remove his own contact'); } catch (CoreCannotSaveObjectException $e) { } UserRights::Logoff(); } public function RemovingOwnContactProvider(): array { return [ 'Administrator' => [1], 'Configuration manager' => [3], ]; } /** * @doesNotPerformAssertions * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testUpgradingToAdmin() { $oUser = $this->CreateUniqueUserAndLogin('test1', 3); try { $this->AddProfileToUser($oUser, 1); $this->fail('Should not be able to upgrade to Administrator'); } catch (CoreCannotSaveObjectException $e) { } catch (CoreException $e) { } // logout $_SESSION = []; UserRights::Logoff(); } /** * @doesNotPerformAssertions * * @throws \CoreException * @throws \DictExceptionUnknownLanguage * @throws \OQLException */ public function testDenyingUserModification() { $oUser = $this->CreateUniqueUserAndLogin('test1', 1); $this->AddProfileToUser($oUser, 3); // Keep only the profile 3 (remove profile 1) $oUserProfile = new URP_UserProfile(); $oUserProfile->Set('profileid', 3); $oUserProfile->Set('reason', 'UNIT Tests'); $oSet = DBObjectSet::FromObject($oUserProfile); $oUser->Set('profile_list', $oSet); try { $oUser->DBWrite(); $this->fail('Should not be able to deny User modifications'); } catch (CoreCannotSaveObjectException $e) { } // logout $_SESSION = []; UserRights::Logoff(); } /** *@dataProvider NonAdminCanListOwnProfilesProvider */ public function testNonAdminCanListOwnProfiles($bHideAdministrators) { utils::GetConfig()->Set('security.hide_administrators', $bHideAdministrators); $oUser = $this->CreateUniqueUserAndLogin('test1', 2); // portal user // List the link between the User and the Profiles $oSearch = new DBObjectSearch('URP_UserProfile'); $oSearch->AddCondition('userid', $oUser->GetKey()); $oSet = new DBObjectSet($oSearch); $this->assertEquals(1, $oSet->Count()); // Get the Profiles as well $oSearch = DBObjectSearch::FromOQL('SELECT URP_Profiles JOIN URP_UserProfile ON URP_UserProfile.profileid = URP_Profiles.id WHERE URP_UserProfile.userid='.$oUser->GetKey()); $oSet = new DBObjectSet($oSearch); $this->assertEquals(1, $oSet->Count()); // logout $_SESSION = []; UserRights::Logoff(); } public function NonAdminCanListOwnProfilesProvider(): array { return [ 'with Admins visible'=> [false], 'with Admins hidden' => [true], ]; } /** * @runInSeparateProcess *@dataProvider NonAdminCannotListAdminProfilesProvider */ public function testNonAdminCannotListAdminProfiles($bHideAdministrators, $iExpectedCount) { utils::GetConfig()->Set('security.hide_administrators', $bHideAdministrators); $oUserAdmin = $this->CreateUser('admin1', 1); $this->CreateUniqueUserAndLogin('test1', 2); // portal user $oSearch = new DBObjectSearch('URP_UserProfile'); $oSearch->AddCondition('userid', $oUserAdmin->GetKey()); $oSet = new DBObjectSet($oSearch); $this->assertEquals($iExpectedCount, $oSet->Count()); // Get the Profiles as well $oSearch = DBObjectSearch::FromOQL('SELECT URP_Profiles JOIN URP_UserProfile ON URP_UserProfile.profileid = URP_Profiles.id WHERE URP_UserProfile.userid='.$oUserAdmin->GetKey()); $oSet = new DBObjectSet($oSearch); $this->assertEquals($iExpectedCount, $oSet->Count()); // logout $_SESSION = []; UserRights::Logoff(); } public function NonAdminCannotListAdminProfilesProvider(): array { return [ 'with Admins visible'=> [false, 1], 'with Admins hidden' => [true, 0], ]; } /** * @dataProvider WithConstraintParameterProvider * @param string $sClass * @param string $sAttCode * @param bool $bExpected * * @return void * @throws \Exception */ public function testWithConstraintParameter(string $sClass, string $sAttCode, bool $bExpected) { $oAttDef = \MetaModel::GetAttributeDef($sClass, $sAttCode); $this->assertTrue(method_exists($oAttDef, "GetHasConstraint")); $this->assertEquals($bExpected, $oAttDef->GetHasConstraint()); } public function WithConstraintParameterProvider() { return [ ['User', 'profile_list', true], ['User', 'allowed_org_list', true], ['Person', 'team_list', false], ]; } }