Skip to content

Commit

Permalink
Fixed issue #15832: Participant settings not imported correctly when …
Browse files Browse the repository at this point in the history
…importing a CSV file (emailstatus issue)
  • Loading branch information
lime-marc authored and ptelu committed Mar 9, 2020
1 parent 2fe2556 commit 8e46ca8
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 16 deletions.
8 changes: 3 additions & 5 deletions application/controllers/admin/tokens.php
Expand Up @@ -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'));
}
}

Expand Down Expand Up @@ -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();
Expand Down
12 changes: 8 additions & 4 deletions application/core/LSSodium.php
Expand Up @@ -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;
}
Expand Down
16 changes: 12 additions & 4 deletions application/models/LSActiveRecord.php
Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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);
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions tests/unit/models/EncryptAttributesTest.php
Expand Up @@ -26,28 +26,34 @@ 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);
$this->assertCount(1, $tokens);
$token = $tokens[0];

// Change lastname.
$token->lastname = 'last';
$token->lastname = $teststring;
$token->encryptSave();

// Load token and decrypt.
$tokens = \TokenDynamic::model(self::$surveyId)->findAll();
$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);
}

/**
Expand Down

0 comments on commit 8e46ca8

Please sign in to comment.