diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index fa61300da..b1fa1c480 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -138,7 +138,14 @@ abstract class AttributeDefinition protected $aCSSClasses; - public function GetType() + private $bIsSensitive = false; + + public function IsSensitive() + { + return $this->bIsSensitive; + } + + public function GetType() { return Dict::S('Core:'.get_class($this)); } @@ -3775,7 +3782,12 @@ class AttributeFinalClass extends AttributeString */ class AttributePassword extends AttributeString implements iAttributeNoGroupBy { - const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; + private $bIsSensitive = true; + public function IsSensitive() + { + return $this->bIsSensitive; + } + const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; /** * Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329) @@ -3851,7 +3863,12 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy */ class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy { - const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; + private $bIsSensitive = true; + public function IsSensitive() + { + return $this->bIsSensitive; + } + const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; static $sKey = null; // Encryption key used for all encrypted fields static $sLibrary = null; // Encryption library used for all encrypted fields @@ -9243,7 +9260,12 @@ class AttributeSubItem extends AttributeDefinition */ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy { - const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; + private $bIsSensitive = true; + public function IsSensitive() + { + return $this->bIsSensitive; + } + const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW; /** * Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329) diff --git a/core/restservices.class.inc.php b/core/restservices.class.inc.php index 67f94fc04..e6ff71b7d 100644 --- a/core/restservices.class.inc.php +++ b/core/restservices.class.inc.php @@ -123,7 +123,7 @@ class ObjectResult $this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput); } - public function SanitizeContent() +/* public function SanitizeContent() { foreach($this->fields as $sAttCode => $value) { @@ -132,12 +132,12 @@ class ObjectResult } catch (Exception $e) { // for special cases like ID continue; } - if ($oAttDef instanceof AttributeEncryptedString || $oAttDef instanceof AttributePassword || $oAttDef instanceof AttributeOneWayPassword) + if ($oAttDef->IsSensitive()) { $this->fields[$sAttCode] = '******'; } } - } + }*/ } @@ -198,7 +198,7 @@ class RestResultWithObjects extends RestResult $this->objects[$sObjKey] = $oObjRes; } - public function SanitizeContent() +/* public function SanitizeContent() { parent::SanitizeContent(); @@ -206,7 +206,7 @@ class RestResultWithObjects extends RestResult { $oObjRes->SanitizeContent(); } - } + }*/ } class RestResultWithRelations extends RestResultWithObjects @@ -708,7 +708,8 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer $sClass = $aJsonData['class']; foreach ($aJsonData['fields'] as $sAttCode => $value) { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); - if ($oAttDef instanceof AttributeEncryptedString || $oAttDef instanceof AttributePassword || $oAttDef instanceof AttributeOneWayPassword) { + var_dump($oAttDef); + if ($oAttDef->IsSensitive()) { $aJsonData['fields'][$sAttCode] = '*****'; } } diff --git a/tests/php-unit-tests/unitary-tests/core/RestServicesTest.php b/tests/php-unit-tests/unitary-tests/core/RestServicesTest.php index ae833237a..71d1b1dd6 100644 --- a/tests/php-unit-tests/unitary-tests/core/RestServicesTest.php +++ b/tests/php-unit-tests/unitary-tests/core/RestServicesTest.php @@ -24,9 +24,12 @@ use CoreException; use CoreServices; use CoreUnexpectedValue; use SimpleGraphException; -use UserLocal; - +/** + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * @backupGlobals disabled + */ class RestServicesTest extends ItopDataTestCase { public function setUp(): void @@ -34,8 +37,6 @@ class RestServicesTest extends ItopDataTestCase parent::setUp(); } - // provider - /** * @return void * @dataProvider providerTestSanitizeJsonInput @@ -47,47 +48,47 @@ class RestServicesTest extends ItopDataTestCase $this->assertEquals($sExpectedJsonDataSanitized, $sOutputJson); } - public function testCoreUpdateSanitization() + public function providerTestSanitizeJsonInput() { - $sJsonData = <<CallCoreRestApi_Internally($sJsonData); - $aJson = json_decode($sOutputJson, true); - $this->assertEquals(0, $aJson['code'], $sOutputJson); // answer is still the same - $this->assertEquals($sExpectedJsonDataSanitized, $aJson['input_data'], $sOutputJson); + return [ + 'core/check_credentials' => [ + '{"operation": "core/check_credentials", "user": "admin", "password": "admin"}', + '{ + "operation": "core/check_credentials", + "user": "admin", + "password": "*****" +}' + ], + 'core/update' => [ + '{"operation": "core/update", "comment": "Update user", "class": "UserLocal", "key": {"id":1}, "output_fields": "first_name, password", "fields": {"password" : "123456"}}', + '{ + "operation": "core/update", + "comment": "Update user", + "class": "UserLocal", + "key": { + "id": 1 + }, + "output_fields": "first_name, password", + "fields": { + "password": "*****" + } +}' + ], + 'core/create' => [ + '{"operation": "core/create", "comment": "Create user", "class": "UserLocal", "fields": {"first_name": "John", "last_name": "Doe", "email": "jd@example/com", "password" : "123456"}}', + '{ + "operation": "core/create", + "comment": "Create user", + "class": "UserLocal", + "fields": { + "first_name": "John", + "last_name": "Doe", + "email": "jd@example/com", + "password": "*****" + } +}' + ], + ]; } /** @@ -102,60 +103,55 @@ JSON; */ public function testSanitizeJsonOutput($sOperation, $aJsonData, $sExpectedJsonDataSanitized) { + $this->CreateUser('my_example', '1', 'Azertyuiiop*12', 1); $oRS = new CoreServices(); - $oUser = new UserLocal(); - $oUser->Set('password', "123456"); - $oRestResultWithObject = new \RestResultWithObjects(); - $oRestResultWithObject->AddObject(0, "ok", $oUser, ['UserLocal' => ['login', 'password']]); - $oRestResultWithObject->SanitizeContent(); - $this->assertEquals($sExpectedJsonDataSanitized, json_encode($oRestResultWithObject)); - } + $oResult = $oRS->ExecOperation(1.3, $sOperation, json_decode(json_encode($aJsonData))); + // delete every pattern like "::xxx" + $actualResult = json_encode($oResult); + $sExpectedJsonDataSanitized = preg_replace('/::[0-9]+/', '', $sExpectedJsonDataSanitized); + $actualResult = preg_replace('/::[0-9]+/', '', $actualResult); + // convert both to arrays + $actualResult = json_decode($actualResult, true); + $sExpectedJsonDataSanitized = json_decode($sExpectedJsonDataSanitized, true); + $this->recursive_unset($actualResult, 'key'); + $this->recursive_unset($sExpectedJsonDataSanitized, 'key'); - public function providerTestSanitizeJsonInput() - { - return [ - 'core/check_credentials' => [ - '{"operation": "core/check_credentials", "user": "admin", "password": "admin"}', - '{ - "operation": "core/check_credentials", - "user": "admin", - "password": "*****" -}' - ], - 'core/update' => [ - '{"operation": "core/update", "comment": "Update user", "class": "UserLocal", "key": {"id":1}, "output_fields": "first_name, password", "fields": {"password" : "123456"}}', - '{"operation": "core/update", "comment": "Update user", "class": "UserLocal", "key": {"id":1}, "output_fields": "first_name, password", "fields": {"password" : "*****"}}' - ], - 'core/create' => [ - '{"operation": "core/create", "comment": "Create user", "class": "UserLocal", "fields": {"first_name": "John", "last_name": "Doe", "email": "jd@example/com", "password" : "123456"}}', - '{"operation": "core/create", "comment": "Create user", "class": "UserLocal", "fields": {"first_name": "John", "last_name": "Doe", "email": "jd@example/com", "password" : "*****"}}', - ], - ]; + + $this->assertEquals($sExpectedJsonDataSanitized, $actualResult); } public function providerTestSanitizeJsonOutput() { return [ - 'core/check_credentials' => [ - 'core/check_credentials', - ['user' => 'admin', 'password' => 'admin'], - '{"operation":"core/check_credentials","user":"admin","password":"*****"}' - ], - 'core/update' => [ - 'core/update', - ['comment' => 'Update user', 'class' => 'UserLocal', 'key' => ['description' => 'My description'], 'output_fields' => 'first_name, password', 'fields' => ['id' => '1', 'password' => '123456']], - '{"operation":"core/update","comment":"Update user","class":"UserLocal","key":{"description":"My description"},"output_fields":"first_name, password","fields":{"id":"1","password":"*****"}}' - ], - 'core/create' => [ - 'core/create', - ['comment' => 'Create user', 'class' => 'UserLocal', 'fields' => ['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'jd@example/com', 'password' => '123456']], - '{"operation":"core/create","comment":"Create user","class":"UserLocal","fields":{"first_name":"John","last_name":"Doe","email":"jd@example/com","password":"*****"}}' - ], - 'core/get' => [ - 'core/get', - ['comment' => 'Get user', 'class' => 'UserLocal', 'key' => ['id' => '1'], 'output_fields' => 'first_name, password'], - '{"operation":"core/get","comment":"Get user","class":"UserLocal","key":{"id":"1"},"output_fields":"first_name, password"}' - ], + + 'core/update' => [ + 'core/update', + ['comment' => 'Update user', 'class' => 'UserLocal', 'key' => ['login' => 'my_example'], 'output_fields' => 'password', 'fields' => ['password' => 'opkB!req57']], + '{"objects":{"UserLocal::78":{"code":0,"message":"updated","class":"UserLocal","key":"78","fields":{"password":"*****"}}},"code":0,"message":null}'], + 'core/create' => [ + 'core/create', + ['comment' => 'Create user', 'class' => 'UserLocal', 'fields' => ['password' => 'Azertyuiiop*12', 'login' => 'toto', 'profile_list' => [1]]], + '{"operation":"core/create","comment":"Create user","class":"UserLocal","fields":{"first_name":"John","last_name":"Doe","email":"jd@example/com","password":"*****"}}' + ], + 'core/get' => [ + 'core/get', + ['comment' => 'Get user', 'class' => 'UserLocal', 'key' => ['login' => 'my_example'], 'output_fields' => 'first_name, password'], + '{"objects":{"UserLocal":{"code":0,"message":"","class":"UserLocal","key":"148","fields":{"first_name":"My first name","password":"*****"}}},"code":0,"message":"Found: 1"}' + ], + 'core/check_credentials' => [ + 'core/check_credentials', + ['user' => 'admin', 'password' => 'admin'], + '{"code":0,"message":null,"authorized":true}' + ], ]; } + + function recursive_unset(&$array, $unwanted_key) { + unset($array[$unwanted_key]); + foreach ($array as &$value) { + if (is_array($value)) { + $this->recursive_unset($value, $unwanted_key); + } + } + } }