Skip to content

Commit

Permalink
DEV: implemented service class IpAddressAnonymizer and implemented th…
Browse files Browse the repository at this point in the history
…e anonymization of ip addresses in active surveys if necessary
  • Loading branch information
Trischi80 committed Apr 15, 2020
1 parent f5ecd23 commit c4716cf
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 61 deletions.
3 changes: 3 additions & 0 deletions application/controllers/admin/database.php
Expand Up @@ -975,6 +975,9 @@ private function actionUpdateSurveyLocaleSettings($iSurveyID)
$oSurvey->savetimings = $this->_filterEmptyFields($oSurvey, 'savetimings');
$oSurvey->datestamp = $this->_filterEmptyFields($oSurvey, 'datestamp');
$oSurvey->ipaddr = $this->_filterEmptyFields($oSurvey, 'ipaddr');
//save the new setting ipanonymize
$oSurvey->ipanonymize = $this->_filterEmptyFields($oSurvey, 'ipanonymize');
//todo: here we have to change the ip-addresses already saved ?!?
$oSurvey->refurl = $this->_filterEmptyFields($oSurvey, 'refurl');
}

Expand Down
19 changes: 3 additions & 16 deletions application/controllers/admin/globalsettings.php
Expand Up @@ -404,29 +404,16 @@ public function surveySettings()
$oSurvey = SurveysGroupsettings::model()->findByPk($gsid);
$oSurvey->setOptions();

// TEST anonymize IP adresses
$ipToAnonymize = "192.168.2.5"; //this one should be anonymized ...
$result = anonymizeIpAddress($ipToAnonymize); // tested OK
// Test already anonymized ip
$result = anonymizeIpAddress($result); // tested OK
// TEST no ip address
$result = anonymizeIpAddress("not.an.ip"); // tested OK
$ipV6 = "2a03:2880:2110:df07:face:b00c::1";
$result = anonymizeIpAddress($ipV6);
$ipV6 = "2a03:2880:2110:df07:face:b00c::";
$result = anonymizeIpAddress($ipV6);
$ipV6 = "2a03::2110:df07:face:b00c::"; //this one is not a valid ipv6
$result = anonymizeIpAddress($ipV6);
$ipV6 = "2a03::2110:df07:face:b00c:3:4"; //this one is not a valid ipv6
$result = anonymizeIpAddress($ipV6);

$sPartial = Yii::app()->request->getParam('partial', '_generaloptions_panel');

if (isset($_POST)) {
$oSurvey->attributes = $_POST;
$oSurvey->gsid = 0;
$oSurvey->usecaptcha = Survey::saveTranscribeCaptchaOptions();

//todo: when changing ipanonymiez from "N" to "Y", call the function that anonymizes the ip-addresses


if ($oSurvey->save()) {
$bRedirect = 1;
}
Expand Down
40 changes: 0 additions & 40 deletions application/helpers/common_helper.php
Expand Up @@ -4878,43 +4878,3 @@ function resourceExtractFilter($p_event, &$p_header) {
return 0;
}
}

/**
* Anonymizes ip address:
* This anonymize an IP address to a /24 subnet (IPv4) or a /64 subnet (IPv6)
* For instance, the IPv4 address 192.168.178.123 is anonymized to 192.168.178.0.
* The IPv6 address 2a03:2880:2110:df07:face:b00c::1 is anonymized to 2a03:2880:2110:df07::
* It also checks before anonymizes if it has already been anonymized and in that case give back
* the ip address without changing it.
*
* @param string $ipAddress
* @return string|boolean if ip is not anonymised false will be returned (in case of not a valid ip or ip has already been
* anonymised), else the anonymise ip will be returned as a string
*/
function anonymizeIpAddress($ipAddress){
$anonymizedIp = false;

if(filter_var($ipAddress,FILTER_VALIDATE_IP,FILTER_FLAG_IPV4)){ //check if it is valid ipv4
$ipArray = explode('.', $ipAddress);
$last_digit = array_pop($ipArray);
if($last_digit!=0){ //check if it has already been anonymized
//set last number to 0
$anonymizedIp = implode('.',$ipArray); //without last digit ?!?
$anonymizedIp .= '.0';
}
} elseif (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { //check if it is valid ipv6
$ipArray = explode(':', $ipAddress);
//the last 5 blocks have to be set to 0 ...
for ($i = 0; $i < 5; $i++) {
array_pop($ipArray);
}

$anonymizedIp = implode(':', $ipArray);
//append last 5 blocks with 0
for ($i = 0; $i < 5; $i++) {
$anonymizedIp .= ':0';
}
}

return $anonymizedIp;
}
8 changes: 8 additions & 0 deletions application/helpers/expressions/em_manager_helper.php
Expand Up @@ -5000,6 +5000,7 @@ public static function StartSurvey($surveyid,$surveyMode='group',$aSurveyOptions
$LEM->surveyOptions['deletenonvalues'] = (isset($aSurveyOptions['deletenonvalues']) ? ($aSurveyOptions['deletenonvalues']=='1') : true);
$LEM->surveyOptions['hyperlinkSyntaxHighlighting'] = (isset($aSurveyOptions['hyperlinkSyntaxHighlighting']) ? $aSurveyOptions['hyperlinkSyntaxHighlighting'] : false);
$LEM->surveyOptions['ipaddr'] = $survey->isIpAddr;
$LEM->surveyOptions['ipAnonymize'] = ($survey->ipanonymize === 'Y');
$LEM->surveyOptions['radix'] = (isset($aSurveyOptions['radix']) ? $aSurveyOptions['radix'] : '.');
$LEM->surveyOptions['refurl'] = (isset($aSurveyOptions['refurl']) ? $aSurveyOptions['refurl'] : NULL);
$LEM->surveyOptions['savetimings'] = $survey->isSaveTimings;
Expand Down Expand Up @@ -5511,6 +5512,13 @@ private function _UpdateValuesInDatabase($finished=false)
if ($this->surveyOptions['ipaddr'] == true)
{
$sdata['ipaddr'] = getIPAddress();
if($this->surveyOptions['ipAnonymize']){
$ipAddressAnonymizer = new LimeSurvey\Models\Services\IpAddressAnonymizer($sdata['ipaddr']);
$result = $ipAddressAnonymizer->anonymizeIpAddress();
if($result){
$sdata['ipaddr'] = $result;
}
}
}
if ($this->surveyOptions['refurl'] == true)
{
Expand Down
2 changes: 1 addition & 1 deletion application/models/Survey.php
Expand Up @@ -46,7 +46,7 @@
* @property string $allowprev Allow backwards navigation (Y/N)
* @property string $printanswers Participants may print answers: (Y/N)
* @property string $ipaddr Whether Participants IP address will be saved: (Y/N)
* @property string $ipanonymize Whether id addresses have been anonymized (Y/N)
* @property string $ipanonymize Whether id addresses should be anonymized (Y/N)
* @property string $refurl Save referrer URL: (Y/N)
* @property string $datecreated Date survey was created (YYYY-MM-DD hh:mm:ss)
* @property string $publicstatistics Public statistics: (Y/N)
Expand Down
10 changes: 8 additions & 2 deletions application/models/SurveysGroupsettings.php
Expand Up @@ -96,13 +96,19 @@ public function rules()
return array(
array('autonumber_start, showsurveypolicynotice, tokenlength, questionindex, navigationdelay, owner_id', 'numerical', 'integerOnly'=>true),
array('admin', 'length', 'max'=>50),
array('anonymized, format, savetimings, datestamp, usecookie, allowregister, allowsave, autoredirect, allowprev, printanswers, ipaddr, refurl, publicstatistics, publicgraphs, listpublic, htmlemail, sendconfirmation, tokenanswerspersistence, assessments, usecaptcha, showxquestions, showgroupinfo, shownoanswer, showqnumcode, showwelcome, showprogress, nokeyboard, alloweditaftercompletion', 'length', 'max'=>1),
array('anonymized, format, savetimings, datestamp, usecookie, allowregister, allowsave, autoredirect, allowprev, printanswers, ipaddr, refurl, publicstatistics, publicgraphs, listpublic, htmlemail, sendconfirmation, tokenanswerspersistence, assessments, usecaptcha, showxquestions, showgroupinfo, shownoanswer, showqnumcode, showwelcome, showprogress, nokeyboard, alloweditaftercompletion, ipanonymize', 'length', 'max'=>1),
array('adminemail, bounce_email', 'length', 'max'=>255),
array('template', 'length', 'max'=>100),
array('expires, startdate, datecreated, attributedescriptions, emailresponseto, emailnotificationto', 'safe'),
// The following rule is used by search().
// @todo Please remove those attributes that should not be searched.
array('gsid, owner_id, admin, expires, startdate, adminemail, anonymized, format, savetimings, template, datestamp, usecookie, allowregister, allowsave, autonumber_start, autoredirect, allowprev, printanswers, ipaddr, refurl, datecreated, showsurveypolicynotice, publicstatistics, publicgraphs, listpublic, htmlemail, sendconfirmation, tokenanswerspersistence, assessments, usecaptcha, bounce_email, attributedescriptions, emailresponseto, emailnotificationto, tokenlength, showxquestions, showgroupinfo, shownoanswer, showqnumcode, showwelcome, showprogress, questionindex, navigationdelay, nokeyboard, alloweditaftercompletion', 'safe', 'on'=>'search'),
array('gsid, owner_id, admin, expires, startdate, adminemail, anonymized, format,
savetimings, template, datestamp, usecookie, allowregister, allowsave, autonumber_start,
autoredirect, allowprev, printanswers, ipaddr, refurl, datecreated, showsurveypolicynotice,
publicstatistics, publicgraphs, listpublic, htmlemail, sendconfirmation, tokenanswerspersistence,
assessments, usecaptcha, bounce_email, attributedescriptions, emailresponseto, emailnotificationto,
tokenlength, showxquestions, showgroupinfo, shownoanswer, showqnumcode, showwelcome, showprogress,
questionindex, navigationdelay, nokeyboard, alloweditaftercompletion', 'safe', 'on'=>'search'),
);
}

Expand Down
93 changes: 93 additions & 0 deletions application/models/services/IpAddressAnonymizer.php
@@ -0,0 +1,93 @@
<?php


namespace LimeSurvey\Models\Services;

/**
* This class offers a function to anonymize ip addresses.
*
* Class IpAddressAnonymizer
* @package LimeSurvey\Models\Services
*/
class IpAddressAnonymizer
{

/** @var string the original ip address*/
private $ipAddress;

/**
* IpAddressAnonymizer constructor.
*
* @param $ipAddress
*/
public function __construct($ipAddress)
{
$this->ipAddress = $ipAddress;
}

/**
* Checks if ip is a valid ipv4
*
* @return mixed
*/
public function isIpv4(){
return filter_var($this->ipAddress,FILTER_VALIDATE_IP,FILTER_FLAG_IPV4);
}

/**
* Checks if ip is a valid ipv6
*
* @return mixed|boolean false if not valid, otherwise the filtered ip address
*/
public function isIpv6(){
return filter_var($this->ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
}

/**
* Checks if ip is a valid ipAddress
*
* @return bool
*/
public function isValidIp(){
return $this->isIpv4() || $this->isIpv6();
}

/**
* Anonymizes ip address:
*
* For instance, the IPv4 address 192.168.178.123 is anonymized to 192.168.178.0.
* The IPv6 address 2a03:2880:2117:df07:face:b00c:5:1 is anonymized to 2a03:2880:2117:0:0:0:0:0
* It also checks before anonymizes if it has already been anonymized and in that case give back
* the ip address without changing it.
*
* @return string|boolean if ip is not anonymized false will be returned (in case of not a valid ip or ip has already been
* anonymized), else the anonymize ip will be returned as a string
*/
public function anonymizeIpAddress(){
$anonymizedIp = false;

if($this->isIpv4()){ //check if it is valid ipv4
$ipArray = explode('.', $this->ipAddress);
$last_digit = array_pop($ipArray);
if($last_digit!=0){ //check if it has already been anonymized
//set last number to 0
$anonymizedIp = implode('.',$ipArray); //without last digit ?!?
$anonymizedIp .= '.0';
}
} elseif ($this->isIpv6()) { //check if it is valid ipv6
$ipArray = explode(':', $this->ipAddress);
//the last 5 blocks have to be set to 0 ...
for ($i = 0; $i < 5; $i++) {
array_pop($ipArray);
}

$anonymizedIp = implode(':', $ipArray);
//append last 5 blocks with 0
for ($i = 0; $i < 5; $i++) {
$anonymizedIp .= ':0';
}
}

return $anonymizedIp;
}
}
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -25,7 +25,8 @@
],
"LimeSurvey\\Menu\\": "application/libraries/MenuObjects/",
"LimeSurvey\\ExtensionInstaller\\": "application/libraries/ExtensionInstaller/",
"LimeSurvey\\Helpers\\": "application/helpers"
"LimeSurvey\\Helpers\\": "application/helpers",
"LimeSurvey\\Models\\Services\\": "application/models/services"
}
},
"require": {
Expand Down
2 changes: 1 addition & 1 deletion third_party/composer/ClassLoader.php
Expand Up @@ -279,7 +279,7 @@ public function isClassMapAuthoritative()
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}

/**
Expand Down
1 change: 1 addition & 0 deletions third_party/composer/autoload_psr4.php
Expand Up @@ -7,6 +7,7 @@

return array(
'LimeSurvey\\PluginManager\\' => array($baseDir . '/application/libraries/PluginManager', $baseDir . '/application/libraries/PluginManager/Storage'),
'LimeSurvey\\Models\\Services\\' => array($baseDir . '/application/models/services'),
'LimeSurvey\\Menu\\' => array($baseDir . '/application/libraries/MenuObjects'),
'LimeSurvey\\Helpers\\' => array($baseDir . '/application/helpers'),
'LimeSurvey\\ExtensionInstaller\\' => array($baseDir . '/application/libraries/ExtensionInstaller'),
Expand Down
5 changes: 5 additions & 0 deletions third_party/composer/autoload_static.php
Expand Up @@ -10,6 +10,7 @@ class ComposerStaticInitddb1a145e450f862353420acc5153e40
'L' =>
array (
'LimeSurvey\\PluginManager\\' => 25,
'LimeSurvey\\Models\\Services\\' => 27,
'LimeSurvey\\Menu\\' => 16,
'LimeSurvey\\Helpers\\' => 19,
'LimeSurvey\\ExtensionInstaller\\' => 30,
Expand All @@ -22,6 +23,10 @@ class ComposerStaticInitddb1a145e450f862353420acc5153e40
0 => __DIR__ . '/../..' . '/application/libraries/PluginManager',
1 => __DIR__ . '/../..' . '/application/libraries/PluginManager/Storage',
),
'LimeSurvey\\Models\\Services\\' =>
array (
0 => __DIR__ . '/../..' . '/application/models/services',
),
'LimeSurvey\\Menu\\' =>
array (
0 => __DIR__ . '/../..' . '/application/libraries/MenuObjects',
Expand Down

0 comments on commit c4716cf

Please sign in to comment.