From b265dd05489d18eda6257a43391c9dd8e4cdb6da Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 10 Sep 2023 19:29:49 +0200 Subject: [PATCH] NEW extrafields password accepts 'dolcrypt' algorithm (reversible algo) --- htdocs/admin/system/dolibarr.php | 4 +- htdocs/core/class/commonobject.class.php | 65 +++++++++++++++++++++--- htdocs/core/lib/security.lib.php | 4 +- htdocs/langs/en_US/admin.lang | 6 ++- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/htdocs/admin/system/dolibarr.php b/htdocs/admin/system/dolibarr.php index afe5d4d3520..925076b1ee7 100644 --- a/htdocs/admin/system/dolibarr.php +++ b/htdocs/admin/system/dolibarr.php @@ -429,10 +429,12 @@ foreach ($configfileparameters as $key => $value) { global $dolibarr_main_cookie_cryptkey, $dolibarr_main_instance_unique_id; $valuetoshow = $dolibarr_main_instance_unique_id ? $dolibarr_main_instance_unique_id : $dolibarr_main_cookie_cryptkey; // Use $dolibarr_main_instance_unique_id first then $dolibarr_main_cookie_cryptkey if (empty($dolibarr_main_prod)) { - print ''; + print ''; print showValueWithClipboardCPButton($valuetoshow, 0, '********'); + print '     '.$langs->trans("ThisValueCanBeReadBecauseInstanceIsNotInProductionMode").''; } else { print '**********'; + print '     '.$langs->trans("SeeConfFile").''; } if (empty($valuetoshow)) { print img_warning("EditConfigFileToAddEntry", 'dolibarr_main_instance_unique_id'); diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index acf2ffbf4cf..f2a1bb49926 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -6156,6 +6156,11 @@ abstract class CommonObject //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]); } + if ($extrafields->attributes[$this->table_element]['type'][$key] == 'password') { + if (preg_match('/^dolcrypt:/', $value)) { + $this->array_options["options_".$key] = dolDecrypt($value); + } + } } } @@ -6348,17 +6353,25 @@ abstract class CommonObject if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) { // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update. $new_array_options[$key] = $this->array_options[$key]; // Value is kept } else { - // var_dump($algo); - $newvalue = dol_hash($this->array_options[$key], $algo); - $new_array_options[$key] = $newvalue; + if ($algo == 'dolcrypt') { // dolibarr reversible encryption + if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) { + $new_array_options[$key] = dolEncrypt($this->array_options[$key]); + } else { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + } else { + $newvalue = dol_hash($this->array_options[$key], $algo); + $new_array_options[$key] = $newvalue; + } } } else { $new_array_options[$key] = $this->array_options[$key]; // Value is kept } + } else { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept } - } else // Common usage - { - $new_array_options[$key] = $this->array_options[$key]; + } else { // Common usage + $new_array_options[$key] = $this->array_options[$key]; // Value is kept } break; case 'date': @@ -6650,10 +6663,12 @@ abstract class CommonObject $value = $this->array_options["options_".$key]; + $attributeKey = $key; $attributeType = $extrafields->attributes[$this->table_element]['type'][$key]; $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key]; $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key]; $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key]; + $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey]; $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key]; // Similar code than into insertExtraFields @@ -6716,6 +6731,44 @@ abstract class CommonObject case 'price': $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]); break; + case 'password': + $new_array_options = array(); + $algo = ''; + if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) { + // If there is an encryption choice, we use it to crypt data before insert + $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']); + $algo = reset($tmparrays); + if ($algo != '') { + //global $action; // $action may be 'create', 'update', 'update_extras'... + //var_dump($action); + //var_dump($this->oldcopy);exit; + if (is_object($this->oldcopy)) { // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value + //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]); + if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) { // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update. + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } else { + if ($algo == 'dolcrypt') { // dolibarr reversible encryption + if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) { + $new_array_options[$key] = dolEncrypt($this->array_options[$key]); + } else { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + } else { + $newvalue = dol_hash($this->array_options[$key], $algo); + $new_array_options[$key] = $newvalue; + } + } + } else { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + } else { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + } else { // Common usage + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + $this->array_options["options_".$key] = $new_array_options[$key]; + break; case 'date': case 'datetime': if (empty($this->array_options["options_".$key])) { diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index 0a2d2572889..583f61b88f5 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -111,8 +111,8 @@ function dolGetRandomBytes($length) * Note: If a backup is restored onto another instance with a different $conf->file->instance_unique_id, then decoded value will differ. * This function is called for example by dol_set_const() when saving a sensible data into database configuration table llx_const. * - * @param string $chain string to encode - * @param string $key If '', we use $conf->file->instance_unique_id + * @param string $chain String to encode + * @param string $key If '', we use $conf->file->instance_unique_id (so $dolibarr_main_instance_unique_id in conf.php) * @param string $ciphering Default ciphering algorithm * @param string $forceseed To force the seed * @return string encoded string diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index afa4341dfe9..3b9a78e20fe 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -458,7 +458,7 @@ ComputedFormula=Computed field ComputedFormulaDesc=You can enter here a formula using other properties of object or any PHP coding to get a dynamic computed value. You can use any PHP compatible formulas including the "?" condition operator, and following global object: $db, $conf, $langs, $mysoc, $user, $objectoffield.
WARNING: If you need properties of an object not loaded, just fetch yourself the object into your formula like in the second example.
Using a computed field means you can't enter yourself any value from interface. Also, if there is a syntax error, the formula may return nothing.

Example of formula:
$objectoffield->id < 10 ? round($objectoffield->id / 2, 2): ($objectoffield->id + 2 * $user->id) * (int) substr($mysoc->zip, 1, 2)

Example to reload object
(($reloadedobj = new Societe($db)) && ($reloadedobj->fetchNoCompute($objectoffield->id) > 0 ? $reloadedobj->array_options['options_extrafieldkey'] * $reloadedobj->capital / 5: '-1')

Other example of formula to force load of object and its parent object:
(($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($objectoffield->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref: 'Parent project not found' Computedpersistent=Store computed field ComputedpersistentDesc=Computed extra fields will be stored in the database, however, the value will only be recalculated when the object of this field is changed. If the computed field depends on other objects or global data this value might be wrong!! -ExtrafieldParamHelpPassword=Leaving this field blank means this value will be stored without encryption (field must be only hidden with star on screen).
Set 'auto' to use the default encryption rule to save password into database (then value read will be the hash only, no way to retrieve original value) +ExtrafieldParamHelpPassword=Leaving this field blank means this value will be stored WITHOUT encryption (field is just hidden with stars on screen).

Enter value 'dolcrypt' to encode value with a reversible encryption algorithm. Clear data can still be known and edited but is encrypted into database.

Enter 'auto' to use the default password encryption algorithm to save the password into database (the readable value will be the hash only, no way to retrieve original value) ExtrafieldParamHelpselect=List of values must be lines with format key,value (where key can't be '0')

for example:
1,value1
2,value2
code3,value3
...

In order to have the list depending on another complementary attribute list:
1,value1|options_parent_list_code:parent_key
2,value2|options_parent_list_code:parent_key

In order to have the list depending on another list:
1,value1|parent_list_code:parent_key
2,value2|parent_list_code:parent_key ExtrafieldParamHelpcheckbox=List of values must be lines with format key,value (where key can't be '0')

for example:
1,value1
2,value2
3,value3
... ExtrafieldParamHelpradio=List of values must be lines with format key,value (where key can't be '0')

for example:
1,value1
2,value2
3,value3
... @@ -2407,4 +2407,6 @@ AllowOnLineSign=Allow On Line signature AtBottomOfPage=At bottom of page FailedAuth=failed authentications MaxNumberOfFailedAuth=Max number of failed authentication in 24h to deny login. -AllowPasswordResetBySendingANewPassByEmail=If a user A has this permission, and if the user A is not an "admin" user is allowed to reset the password of any other user B, the new password will be send to the email of the other user B but it won't be visible to A. If the user A has the "admin" flag, he will also be able to know what is the new generated password of B so he will be able to take control of the B user account. \ No newline at end of file +AllowPasswordResetBySendingANewPassByEmail=If a user A has this permission, and if the user A is not an "admin" user is allowed to reset the password of any other user B, the new password will be send to the email of the other user B but it won't be visible to A. If the user A has the "admin" flag, he will also be able to know what is the new generated password of B so he will be able to take control of the B user account. +ThisValueCanBeReadBecauseInstanceIsNotInProductionMode=This value can be read because your instance is not set in production mode +SeeConfFile=See inside conf.php file on the server \ No newline at end of file