diff --git a/application/controllers/admin/tokens.php b/application/controllers/admin/tokens.php index b36b3417f13..5a07f22e580 100644 --- a/application/controllers/admin/tokens.php +++ b/application/controllers/admin/tokens.php @@ -411,11 +411,10 @@ public function editMultiple() // Email if (trim(Yii::app()->request->getPost('email', 'lskeep')) != 'lskeep') { - $isValid = preg_match('/^([a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+))(,([a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)))*$/', Yii::app()->request->getPost('email')); - if ($isValid) { - $aData['email'] = Yii::app()->request->getPost('email'); + if (trim(Yii::app()->request->getPost('email')) == '') { + $aData['email'] = null; } else { - $aData['email'] = 'lskeep'; + $aData['email'] = trim(Yii::app()->request->getPost('email')); } } @@ -1950,7 +1949,6 @@ public function import($iSurveyId) $aData['title_bar']['title'] = $survey->currentLanguageSettings->surveyls_title." (".gT("ID").":".$iSurveyId.")"; $aData['sidemenu']["token_menu"] = true; $aData['token_bar']['closebutton']['url'] = 'admin/tokens/sa/index/surveyid/'.$iSurveyId; - $aData['topBar']['showSaveButton'] = true; $aData['topBar']['closeButtonUrl'] = Yii::app()->createUrl('admin/tokens/sa/index/surveyid/'.$iSurveyId); App()->getClientScript()->registerScriptFile(App()->getConfig('adminscripts').'tokensimport.js'); $aEncodings = aEncodingsArray(); diff --git a/application/core/LSSodium.php b/application/core/LSSodium.php index 1013bed928d..75cd8f17fa8 100644 --- a/application/core/LSSodium.php +++ b/application/core/LSSodium.php @@ -101,15 +101,19 @@ public function encrypt($sDataToEncrypt){ * * Decrypt encrypted string. * @param string $sEncryptedString Encrypted string to decrypt - * @param bool $bReturnFalseIfError false by default. If TRUE, return false in case of error (bad decryption). Else, return given $encryptedInput value - * @return string Return decrypted value (string or unsezialized object) if suceeded. Return FALSE if an error occurs (bad password/salt given) or inpyt encryptedString + * @param bool $bReturnInputIfError FALSE by default. If TRUE, return given $sEncryptedString in case of error (bad input/password/salt). Else, throw exception in case of error (bad input/password/salt). + * @return string Return decrypted value (string or unsezialized object) if suceeded. Throw exception if an error occurs (bad input/password/salt) or given $sEncryptedString */ - public function decrypt($sEncryptedString, $bReturnFalseIfError=false){ + public function decrypt($sEncryptedString, $bReturnInputIfError=false){ if ($this->bLibraryExists === true){ if (!empty($sEncryptedString) && $sEncryptedString != 'null'){ $plaintext = ParagonIE_Sodium_Compat::crypto_sign_open(base64_decode($sEncryptedString), $this->sEncryptionPublicKey); if ($plaintext === false){ - throw new SodiumException(sprintf(gT("Wrong decryption key! Decryption key has changed since this data were last saved, so data can't be decrypted. Please consult our manual at %s.", 'unescaped'), 'https://manual.limesurvey.org/Data_encryption#Errors')); + if ($bReturnInputIfError === false){ + throw new SodiumException(sprintf(gT("Wrong decryption key! Decryption key has changed since this data were last saved, so data can't be decrypted. Please consult our manual at %s.", 'unescaped'), 'https://manual.limesurvey.org/Data_encryption#Errors')); + } else { + return $sEncryptedString; + } } else { return $plaintext; } diff --git a/application/models/LSActiveRecord.php b/application/models/LSActiveRecord.php index 51412a8847c..aec8c578996 100644 --- a/application/models/LSActiveRecord.php +++ b/application/models/LSActiveRecord.php @@ -387,10 +387,13 @@ public function encrypt() /** * Encrypt values before saving to the database */ - public function encryptSave($runValidation=false) + public function encryptSave($runValidation=true) { // run validation on attribute values before encryption take place, it is impossible to validate encrypted values if ($runValidation){ + // decrypt attributes before validation, return input value if decryption failed (ie because of unencrypted value in encrypted field) + $this->decryptEncryptAttributes('decrypt', $bReturnInputIfError=true); + // validate attributes if(!$this->validate()) { return false; } @@ -406,7 +409,7 @@ public function encryptSave($runValidation=false) /** * Encrypt/decrypt values */ - public function decryptEncryptAttributes($action = 'decrypt') + public function decryptEncryptAttributes($action = 'decrypt', $bReturnInputIfError=false) { // load sodium library $sodium = Yii::app()->sodium; @@ -416,13 +419,18 @@ public function decryptEncryptAttributes($action = 'decrypt') $aParticipantAttributes = CHtml::listData(ParticipantAttributeName::model()->findAll(array("select" => "attribute_id", "condition" => "encrypted = 'Y' and core_attribute <> 'Y'")), 'attribute_id', ''); foreach ($aParticipantAttributes as $attribute => $value) { if (array_key_exists($this->attribute_id, $aParticipantAttributes)) { - $this->value = $sodium->$action($this->value); + $this->value = $sodium->$action($this->value, $bReturnInputIfError); } } } else { $attributes = $this->encryptAttributeValues($this->attributes, true, false); foreach ($attributes as $key => $attribute) { - $this->$key = $sodium->$action($attribute); + // check if the attribute should be decrypted and can be decrypted (maybe plaintexts in crypted fields) + if ($action == 'decrypt' && strlen(base64_decode($attribute)) < 64) { + // attribute is not a valid base64 encoded value and can not be decrypted + } else { + $this->$key = $sodium->$action($attribute, $bReturnInputIfError); + } } } } diff --git a/tests/unit/models/EncryptAttributesTest.php b/tests/unit/models/EncryptAttributesTest.php index 6ed3354233c..d55db8c9293 100644 --- a/tests/unit/models/EncryptAttributesTest.php +++ b/tests/unit/models/EncryptAttributesTest.php @@ -26,6 +26,12 @@ public static function setupBeforeClass() */ public function testToken() { + function generateRandomString($length = 10) { + return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1,$length); + } + + $teststring = generateRandomString(128); + // Get our token. $tokens = \TokenDynamic::model(self::$surveyId)->findAll(); $this->assertNotEmpty($tokens); @@ -33,7 +39,7 @@ public function testToken() $token = $tokens[0]; // Change lastname. - $token->lastname = 'last'; + $token->lastname = $teststring; $token->encryptSave(); // Load token and decrypt. @@ -41,13 +47,13 @@ public function testToken() $this->assertCount(1, $tokens); $token = $tokens[0]; $token->decrypt(); - $this->assertEquals('last', $token->lastname); + $this->assertEquals($teststring, $token->lastname); // Test the omitting decrypt() works. $tokens = \TokenDynamic::model(self::$surveyId)->findAll(); $this->assertCount(1, $tokens); $token = $tokens[0]; - $this->assertNotEquals('last', $token->lastname); + $this->assertNotEquals($teststring, $token->lastname); } /**