Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
# Conflicts:
#	application/config/version.php
#	application/helpers/update/updates/Update_617.php
#	application/views/themeOptions/options_core.php
  • Loading branch information
ptelu committed Nov 17, 2023
2 parents bfceadb + 5c210ea commit 9e2fa14
Show file tree
Hide file tree
Showing 77 changed files with 948 additions and 713 deletions.
2 changes: 1 addition & 1 deletion application/config/internal.php
Expand Up @@ -156,7 +156,7 @@
'noCsrfValidationParams' => array(),
'noCsrfValidationRoutes' => array(
'rest',
'remotecontrol',
'admin/remotecontrol',
'plugins/unsecure',
),
'csrfCookie' => array(
Expand Down
4 changes: 2 additions & 2 deletions application/config/version.php
Expand Up @@ -12,9 +12,9 @@
*/

$config['versionnumber'] = '6.4.0-dev';
$config['dbversionnumber'] = 617;
$config['dbversionnumber'] = 618;
$config['buildnumber'] = '';
$config['updatable'] = true;
$config['templateapiversion'] = 3;
$config['assetsversionnumber'] = '30361';
$config['assetsversionnumber'] = '30363';
return $config;
14 changes: 12 additions & 2 deletions application/controllers/SurveyAdministrationController.php
Expand Up @@ -53,6 +53,16 @@ public function accessRules()
];
}

/**
* Set filters for all actions
* @return string[]
*/
public function filters()
{
return [
'postOnly + copy'
];
}
/**
* SurveyAdministrationController constructor.
* @param $id
Expand Down Expand Up @@ -2272,7 +2282,7 @@ public function actionCopy()

//maybe thing about permission check for copy surveys
//at the moment dropDown selection shows only surveys for the user he owns himself ...
$action = Yii::app()->request->getParam('action');
$action = Yii::app()->request->getPost('action');
$iSurveyID = sanitize_int(Yii::app()->request->getParam('sid'));
$aData = [];

Expand Down Expand Up @@ -2309,7 +2319,7 @@ public function actionCopy()
$aData['bFailed'] = true;
}
} elseif ($action == 'copysurvey') {
$iSurveyID = sanitize_int(Yii::app()->request->getParam('copysurveylist'));
$iSurveyID = sanitize_int(App()->request->getPost('copysurveylist'));
$aExcludes = array();
if (Yii::app()->request->getPost('copysurveyexcludequotas') == "1") {
$aExcludes['quotas'] = true;
Expand Down
119 changes: 60 additions & 59 deletions application/controllers/UserManagementController.php
Expand Up @@ -1018,21 +1018,23 @@ public function actionBatchSendAndResetLoginData()
$aResults[$user]['title'] = $oUser->users_name;
//User should not reset and resend email to himself throw massive action
if ($oUser->uid == Yii::app()->user->id) {
$aResults[$user]['result'] = false;
$aResults[$user]['error'] = gT("Error! Please change your password from your profile settings.");
} else {
//not modify original superuser
if ($oUser->uid == 1) {
$aResults[$user]['error'] = gT("Error! You do not have the permission to edit this user.");
} else {
$passwordManagement = new \LimeSurvey\Models\Services\PasswordManagement($oUser);
$successData = $passwordManagement->sendPasswordLinkViaEmail(\LimeSurvey\Models\Services\PasswordManagement::EMAIL_TYPE_RESET_PW);
$success = $successData['success'];
if (!$success) {
$aResults[$user]['error'] = sprintf(gT("Error: New password could not be sent to %s"), $oUser->email);
}
$aResults[$user]['result'] = $success;
}
continue;
}
$userManager = new UserManager(Yii::app()->user, $oUser);
if (!$userManager->canEdit()) {
$aResults[$user]['result'] = false;
$aResults[$user]['error'] = gT("Error! You do not have the permission to edit this user.");
continue;
}
$passwordManagement = new \LimeSurvey\Models\Services\PasswordManagement($oUser);
$successData = $passwordManagement->sendPasswordLinkViaEmail(\LimeSurvey\Models\Services\PasswordManagement::EMAIL_TYPE_RESET_PW);
$success = $successData['success'];
if (!$success) {
$aResults[$user]['error'] = sprintf(gT("Error: New password could not be sent to %s"), $oUser->email);
}
$aResults[$user]['result'] = $success;
}

$tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));
Expand Down Expand Up @@ -1066,8 +1068,22 @@ public function actionBatchPermissions()
$aPermissions = Yii::app()->request->getPost('Permission', []);
$results = [];
foreach ($userIds as $iUserId) {
$aPermissionsResults = $this->applyPermissionFromArray($iUserId, $aPermissions);
$oUser = User::model()->findByPk($iUserId);
$results[$iUserId] = [
'title' => $oUser->users_name
];
if ($oUser->uid == Yii::app()->user->id) {
$aResults[$iUserId]['result'] = false;
$aResults[$iUserId]['error'] = gT("You can not update your own permission.");
continue;
}
$userManager = new UserManager(Yii::app()->user, $oUser);
if (!$userManager->canAssignPermissions()) {
$results[$iUserId]['result'] = false;
$results[$iUserId]['error'] = gT("You are not allowed to assign permissions to this user.");
continue;
}
$aPermissionsResults = $this->applyPermissionFromArray($iUserId, $aPermissions);
$oUser->modified = date('Y-m-d H:i:s');
$results[$iUserId]['result'] = $oUser->save();
$results[$iUserId]['title'] = $oUser->users_name;
Expand Down Expand Up @@ -1104,47 +1120,29 @@ public function actionBatchPermissions()
public function actionBatchAddGroup()
{
if (!Permission::model()->hasGlobalPermission('users', 'update')) {
return $this->renderPartial(
'partial/error',
['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
);
throw new CHttpException(403, gT("You do not have permission to access this page."));
}
$aItems = json_decode(Yii::app()->request->getPost('sItems', '')) ?? [];
$iUserGroupId = Yii::app()->request->getPost('addtousergroup');

if ($iUserGroupId) {
$oUserGroup = UserGroup::model()->findByPk($iUserGroupId);
/* check if have permission */
if (
Permission::model()->hasGlobalPermission('usergroups', 'update') /* Global update permission @see UserGroupController->actionEdit */
|| $oUserGroup->requestEditGroup($oUserGroup->ugid, Yii::app()->session['loginID']) /* This user group permission */
) {
$aResults = [];
foreach ($aItems as $sItem) {
$aResults[$sItem]['title'] = '';
$model = $this->loadModel($sItem);
$aResults[$sItem]['title'] = $model->users_name;
if (!$oUserGroup->hasUser($sItem)) {
$aResults[$sItem]['result'] = $oUserGroup->addUser($sItem);
} else {
$aResults[$sItem]['result'] = false;
$aResults[$sItem]['error'] = gT('User is already a member of the group.');
}
}
$iUserGroupId = App()->request->getPost('addtousergroup');
$oUserGroup = UserGroup::model()->findByPk($iUserGroupId);
if (!$oUserGroup) {
throw new CHttpException(404, gT("Group not found"));
}
/* check if have permission */
if (!Permission::model()->hasGlobalPermission('superadmin', 'read') && $oUserGroup->requestEditGroup($oUserGroup->ugid, App()->getCurrentUserId())) {
throw new CHttpException(403, gT("You do not have permission to access this page."));
}

$aResults = [];
foreach ($aItems as $sItem) {
$aResults[$sItem]['title'] = '';
$model = $this->loadModel($sItem);
$aResults[$sItem]['title'] = $model->users_name;
if (!$oUserGroup->hasUser($sItem)) {
$aResults[$sItem]['result'] = $oUserGroup->addUser($sItem);
} else {
$aResults[0] = [
'title' => gT("All"),
'result' => false,
'error' => gT('You don\'t have permission on this group.')
];
}
} else {
foreach ($aItems as $sItem) {
$aResults[$sItem]['title'] = '';
$model = $this->loadModel($sItem);
$aResults[$sItem]['title'] = $model->users_name;
$aResults[$sItem]['result'] = false;
$aResults[$sItem]['error'] = gT('No user group selected.');
$aResults[$sItem]['error'] = gT('User is already a member of the group.');
}
}

Expand All @@ -1169,11 +1167,8 @@ public function actionBatchAddGroup()
public function actionBatchApplyRoles()
{
/* Need super admin roles */
if (!Permission::model()->hasGlobalPermission('superadmin')) {
return $this->renderPartial(
'partial/error',
['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
);
if (!Permission::model()->hasGlobalPermission('superadmin', 'create')) {
throw new CHttpException(403, gT("You do not have permission to access this page."));
}
$aItems = json_decode(Yii::app()->request->getPost('sItems', '')) ?? [];
$aUserRoleIds = Yii::app()->request->getPost('roleselector');
Expand All @@ -1183,16 +1178,22 @@ public function actionBatchApplyRoles()
$aResults[$sItem]['title'] = '';
$model = $this->loadModel($sItem);
$aResults[$sItem]['title'] = $model->users_name;
if (Permission::isForcedSuperAdmin($sItem)) {
/* Show an error for forced super admin, this don't disable for DB superadmin */
if ($model->uid == Yii::app()->user->id) {
$aResults[$sItem]['result'] = false;
$aResults[$sItem]['error'] = gT('The superadmin role cannot be changed.');
$aResults[$sItem]['error'] = gT("You can not update your own roles.");
continue;
}
$userManager = new UserManager(Yii::app()->user, $model);
if (!$userManager->canAssignRole()) {
$aResults[$sItem]['result'] = false;
$aResults[$sItem]['error'] = gT('You can not set role to this user.');
} else {
foreach ($aUserRoleIds as $iUserRoleId) {
$aResults[$sItem]['result'] = Permissiontemplates::model()->applyToUser($sItem, $iUserRoleId);
}
}
}

$tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

Yii::app()->getController()->renderPartial(
Expand Down
2 changes: 2 additions & 0 deletions application/controllers/admin/Database.php
Expand Up @@ -295,6 +295,8 @@ private function actionUpdateSurveyLocaleSettings($surveyId, $input = [])
Yii::app()
->setFlashMessage(gT('Survey settings were successfully saved.'));
} catch (PersistErrorException $e) {
// @todo: Should we be catching only this kind of exceptions or all Throwable?
// BUt that could show sensitive information
Yii::app()->setFlashMessage(
$e->getMessage(),
'error'
Expand Down
4 changes: 2 additions & 2 deletions application/controllers/admin/ParticipantsAction.php
Expand Up @@ -1059,7 +1059,7 @@ public function uploadCSV()
}
if ($thisduplicate == 1) {
$dupfound = true;
$duplicatelist[] = $writearray['firstname'] . " " . $writearray['lastname'] . " (" . $writearray['email'] . ")";
$duplicatelist[] = CHtml::encode($writearray['firstname'] . " " . $writearray['lastname'] . " (" . $writearray['email'] . ")");
}

//Checking the email address is in a valid format
Expand All @@ -1071,7 +1071,7 @@ public function uploadCSV()
$sEmailaddress = $aEmailAddresses[0];
if (!validateEmailAddress($sEmailaddress)) {
$invalidemail = true;
$invalidemaillist[] = $line[0] . " " . $line[1] . " (" . $line[2] . ")";
$invalidemaillist[] = CHtml::encode($line[0] . " " . $line[1] . " (" . $line[2] . ")");
}
}
if (!$dupfound && !$invalidemail) {
Expand Down
1 change: 1 addition & 0 deletions application/core/LSHttpRequest.php
Expand Up @@ -264,6 +264,7 @@ public static function routeMatchesNoCsrfValidationRule($route, $rule)
// For example the routes "rest" (in the case of "index.php/rest?...") or "rest/..." (in the case of
// "index.php/rest/...") should be matched by the rule "rest", but the route "admin/menus/sa/restore"
// should not.
$route = ltrim($route, '/');
return preg_match('#^' . $rule . '$|^' . $rule . '/#', (string) $route);
}

Expand Down
20 changes: 18 additions & 2 deletions application/core/LSYii_Validators.php
Expand Up @@ -119,11 +119,15 @@ public function fixCKeditor($value)
/**
* Remove any script or dangerous HTML
*
* @param string $value
* @param null|string $value
* @return string
*/
public function xssFilter($value)
{
/* No need to filter empty $value */
if (empty($value)) {
return strval($value);
}
$filter = LSYii_HtmlPurifier::getXssPurifier();

/** Start to get complete filtered value with url decode {QCODE} (bug #09300). This allow only question number in url, seems OK with XSS protection **/
Expand Down Expand Up @@ -166,10 +170,14 @@ public function xssFilter($value)
* Defines the customs validation rule for language string
*
* @param mixed $value
* @return mixed
* @return string
*/
public function languageFilter($value)
{
/* No need to filter empty $value */
if (empty($value)) {
return strval($value);
}
// Maybe use the array of language ?
return preg_replace('/[^a-z0-9-]/i', '', (string) $value);
}
Expand All @@ -182,6 +190,10 @@ public function languageFilter($value)
*/
public function multiLanguageFilter($value)
{
/* No need to filter empty $value */
if (empty($value)) {
return strval($value);
}
$aValue = explode(" ", trim((string) $value));
$aValue = array_map("sanitize_languagecode", $aValue);
return implode(" ", $aValue);
Expand All @@ -194,6 +206,10 @@ public function multiLanguageFilter($value)
*/
public static function isXssUrl($url)
{
/* No need to filter empty $value */
if (empty($url)) {
return false;
}
$decodedUrl = self::treatSpecialChars($url);
$clean = self::removeInvisibleChars($decodedUrl);

Expand Down
3 changes: 3 additions & 0 deletions application/helpers/common_helper.php
Expand Up @@ -5050,6 +5050,9 @@ function recursive_preg_replace($pattern, $replacement, $subject, $limit = -1, &
if ($recursion_limit < 0) {
return $subject;
}
if (empty($subject)) {
return $subject;
}
$result = preg_replace($pattern, $replacement, $subject, $limit, $count);
if ($count > 0) {
$result = recursive_preg_replace($pattern, $replacement, $result, $limit, $auxCount, --$recursion_limit);
Expand Down
13 changes: 8 additions & 5 deletions application/helpers/expressions/em_manager_helper.php
Expand Up @@ -4125,7 +4125,7 @@ public function ProcessAllNeededRelevance($onlyThisQseq = null, $groupSeq = null

/**
* Translate all Expressions, Macros, registered variables, etc. in $string
* @param string $string - the string to be replaced
* @param string|null $string - the string to be replaced
* @param integer $questionNum - the $qid of question being replaced - needed for properly alignment of question-level relevance and tailoring
* @param array|null $replacementFields - optional replacement values
* @param integer $numRecursionLevels - the number of times to recursively subtitute values in this string
Expand All @@ -4140,9 +4140,9 @@ public static function ProcessString($string, $questionNum = null, $replacementF
$now = microtime(true);
$LEM =& LimeExpressionManager::singleton();

if ($noReplacements) {
$LEM->em->SetPrettyPrintSource($string);
return $string;
if ($noReplacements || empty($string)) {
$LEM->em->SetPrettyPrintSource(strval($string));
return strval($string);
}
if (!empty($replacementFields) && is_array($replacementFields)) {
self::updateReplacementFields($replacementFields);
Expand All @@ -4166,14 +4166,17 @@ public static function ProcessString($string, $questionNum = null, $replacementF

/**
* Translate all Expressions, Macros, registered variables, etc. in $string for current step
* @param string $string - the string to be replaced
* @param string|null $string - the string to be replaced
* @param array $replacementFields - optional replacement values
* @param integer $numRecursionLevels - the number of times to recursively subtitute values in this string
* @param boolean $static - return static string (without any javascript)
* @return string - the original $string with all replacements done.
*/
public static function ProcessStepString($string, $replacementFields = [], $numRecursionLevels = 3, $static = false)
{
if (empty($string)) {
return strval($string);
}
if ((strpos($string, "{") === false)) {
return $string;
}
Expand Down

0 comments on commit 9e2fa14

Please sign in to comment.