Added complete GDPR compliance features

git-svn-id: svn://192.168.202.10@2936 3d104415-ff17-0410-8863-d5cf3c621b8a
This commit is contained in:
mattf
2018-03-02 21:10:53 +00:00
parent 980f95a91b
commit c78fdbc3b8
3 changed files with 458 additions and 2 deletions
+320
View File
@@ -0,0 +1,320 @@
#!/usr/bin/perl
#
# AST_GDPR_audio_purge.pl
#
# This script queries the recording log deletion queue and purges any audio recordings
# inserted into this table (done through the GDPR feature in the administrator interface)
# Set to purge nightly, or as often as desired
#
# 1 0 * * * /usr/share/astguiclient/AST_GDPR_audio_purge.pl --recording_dir=/var/spool/asterisk/monitorDONE
#
#
# FLAG FOR RECORDING DIRECTORY ON SERVER
# *** REQUIRED ***
# --recording_dir
#
# FLAG FOR NO DATE DIRECTORY ON SERVER
# --nodatedir
#
# FLAG FOR Y/M/D DATE DIRECTORY ON SERVER
# --YMDdatedir
#
# PERFORM ADDITIONAL DIRECTORY SEARCHES
# --location_search
#
# Copyright (C) 2018 Matt Florell <vicidial@gmail.com>, Joe Johnson <freewermadmin@gmail.com> LICENSE: AGPLv2
#
# 180223-0120 - First Build
# 180228-0200 - Added location_search option
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year = ($year + 1900);
$mon++;
if ($hour < 10) {$hour = "0$hour";}
if ($min < 10) {$min = "0$min";}
if ($sec < 10) {$sec = "0$sec";}
if ($mon < 10) {$mon = "0$mon";}
if ($mday < 10) {$mday = "0$mday";}
$FTPdate = "$year-$mon-$mday";
# default path to astguiclient configuration file:
$PATHconf = '/etc/astguiclient.conf';
open(conf, "$PATHconf") || die "can't open $PATHconf: $!\n";
@conf = <conf>;
close(conf);
$i=0;
foreach(@conf)
{
$line = $conf[$i];
$line =~ s/ |>|\n|\r|\t|\#.*|;.*//gi;
if ( ($line =~ /^PATHhome/) && ($CLIhome < 1) )
{$PATHhome = $line; $PATHhome =~ s/.*=//gi;}
if ( ($line =~ /^PATHlogs/) && ($CLIlogs < 1) )
{$PATHlogs = $line; $PATHlogs =~ s/.*=//gi;}
if ( ($line =~ /^PATHagi/) && ($CLIagi < 1) )
{$PATHagi = $line; $PATHagi =~ s/.*=//gi;}
if ( ($line =~ /^PATHweb/) && ($CLIweb < 1) )
{$PATHweb = $line; $PATHweb =~ s/.*=//gi;}
if ( ($line =~ /^PATHsounds/) && ($CLIsounds < 1) )
{$PATHsounds = $line; $PATHsounds =~ s/.*=//gi;}
if ( ($line =~ /^PATHmonitor/) && ($CLImonitor < 1) )
{$PATHmonitor = $line; $PATHmonitor =~ s/.*=//gi;}
if ( ($line =~ /^PATHDONEmonitor/) && ($CLIDONEmonitor < 1) )
{$PATHDONEmonitor = $line; $PATHDONEmonitor =~ s/.*=//gi;}
if ( ($line =~ /^VARserver_ip/) && ($CLIserver_ip < 1) )
{$VARserver_ip = $line; $VARserver_ip =~ s/.*=//gi;}
if ( ($line =~ /^VARDB_server/) && ($CLIDB_server < 1) )
{$VARDB_server = $line; $VARDB_server =~ s/.*=//gi;}
if ( ($line =~ /^VARDB_database/) && ($CLIDB_database < 1) )
{$VARDB_database = $line; $VARDB_database =~ s/.*=//gi;}
if ( ($line =~ /^VARDB_user/) && ($CLIDB_user < 1) )
{$VARDB_user = $line; $VARDB_user =~ s/.*=//gi;}
if ( ($line =~ /^VARDB_pass/) && ($CLIDB_pass < 1) )
{$VARDB_pass = $line; $VARDB_pass =~ s/.*=//gi;}
if ( ($line =~ /^VARDB_port/) && ($CLIDB_port < 1) )
{$VARDB_port = $line; $VARDB_port =~ s/.*=//gi;}
if ( ($line =~ /^VARFTP_host/) && ($CLIFTP_host < 1) )
{$VARFTP_host = $line; $VARFTP_host =~ s/.*=//gi;}
if ( ($line =~ /^VARFTP_user/) && ($CLIFTP_user < 1) )
{$VARFTP_user = $line; $VARFTP_user =~ s/.*=//gi;}
if ( ($line =~ /^VARFTP_pass/) && ($CLIFTP_pass < 1) )
{$VARFTP_pass = $line; $VARFTP_pass =~ s/.*=//gi;}
if ( ($line =~ /^VARFTP_port/) && ($CLIFTP_port < 1) )
{$VARFTP_port = $line; $VARFTP_port =~ s/.*=//gi;}
if ( ($line =~ /^VARFTP_dir/) && ($CLIFTP_dir < 1) )
{$VARFTP_dir = $line; $VARFTP_dir =~ s/.*=//gi;}
if ( ($line =~ /^VARHTTP_path/) && ($CLIHTTP_path < 1) )
{$VARHTTP_path = $line; $VARHTTP_path =~ s/.*=//gi;}
$i++;
}
### begin parsing run-time options ###
if (length($ARGV[0])>1)
{
$i=0;
while ($#ARGV >= $i)
{
$args = "$args $ARGV[$i]";
$i++;
}
if ($args =~ /--help/i)
{
print "allowed run time options:\n";
print " [--help] = this screen\n";
print " [--debug] = debug\n";
print " [--debugX] = super debug\n";
print " [--test] = test\n";
print " [--recording_dir] = base directory to look for queued recordings (REQUIRED)\n";
print " [--nodatedir] = do not look in dated sub-directories in the base directory\n";
print " [--YMDdatedir] = look in Year/Month/Day dated sub-directories in the base directory\n";
print " [--location_search] = if recording is not found using command line setting, search\n";
print " using the remaining 2 directory options\n";
print "\n";
exit;
}
else
{
if ($args =~ /--debug/i)
{
$DB=1;
print "\n----- DEBUG -----\n\n";
}
if ($args =~ /--debugX/i)
{
$DBX=1;
print "\n----- SUPER DEBUG -----\n\n";
}
if ($args =~ /--test/i)
{
$T=1; $TEST=1;
print "\n----- TESTING -----\n\n";
}
if ($args =~ /--YMDdatedir/i)
{
$YMDdatedir=1;
if ($DB) {print "\n----- Y/M/D DATED DIRECTORIES -----\n\n";}
}
if ($args =~ /--nodatedir/i)
{
$nodatedir=1;
if ($DB) {print "\n----- NO DATE DIRECTORIES -----\n\n";}
}
if ($args =~ /--location_search/i)
{
$location_search=1;
if ($DB) {print "\n----- SEARCH VIA LOCATION IF NOT FOUND -----\n\n";}
}
if ($args =~ /--recording_dir=/i)
{
my @data_in = split(/--recording_dir=/,$args);
$recording_dir = $data_in[1];
$recording_dir =~ s/ .*//gi;
if ($DB > 0)
{print "\n----- MAIN RECORDING DIRECTORY: $recording_dir -----\n\n";}
}
}
}
if (!$recording_dir || (!-d "$recording_dir") )
{
print "Script requires recording directory to be set and that the directory exists.\n";
exit;
}
# Customized Variables
$server_ip = $VARserver_ip; # Asterisk server IP
if (!$VARDB_port) {$VARDB_port='3306';}
use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second
use DBI;
$dbhA = DBI->connect("DBI:mysql:$VARDB_database:$VARDB_server:$VARDB_port", "$VARDB_user", "$VARDB_pass")
or die "Couldn't connect to database: " . DBI->errstr;
$dbhB = DBI->connect("DBI:mysql:$VARDB_database:$VARDB_server:$VARDB_port", "$VARDB_user", "$VARDB_pass")
or die "Couldn't connect to database: " . DBI->errstr;
$stmt="select * from recording_log_deletion_queue where date_deleted is null order by date_queued asc";
if ($DBX) {print "\n$stmt\n";}
$rslt=$dbhA->prepare($stmt);
$rslt->execute();
if ($rslt->rows>0)
{
while (@row=$rslt->fetchrow_array)
{
$recording_id=$row[0];
$filename=$row[2];
$location=$row[3];
if ($DB) {print " - $filename\n";}
if ($nodatedir)
{
$start_time_dir="";
}
else
{
$et_stmt="select substr(start_time, 1, 10) from recording_log where recording_id='$recording_id'";
if ($DBX) {print " - $et_stmt\n";}
$et_rslt=$dbhB->prepare($et_stmt);
$et_rslt->execute();
if ($et_rslt->rows>0)
{
@et_row=$et_rslt->fetchrow_array;
if (!$YMDdatedir)
{
$start_time_dir="/".$et_row[0];
}
else
{
@et_array=split(/\-/, $et_row[0]);
$start_time_dir="/".$et_array[0]."/".$et_array[1]."/".$et_array[2];
}
}
$et_rslt->finish();
}
$filepath=$recording_dir.$start_time_dir."/".$filename."*";
$filepath=~s/\/+/\//gi;
if ($DB) {print " -- SEARCHING FOR $filepath\n";}
@found_files = glob("$filepath");
if (scalar(@found_files)>0)
{
for ($i=0; $i<scalar(@found_files); $i++)
{
if ($DB) {print " --- $found_files[$i] FOUND, DELETING...\n\n";}
$upd_stmt="update recording_log_deletion_queue set date_deleted=now() where recording_id='$recording_id'";
if ($T)
{
if ($DBX) {print "\n$upd_stmt\n";}
}
else
{
`rm $found_files[$i]`;
$upd_rslt=$dbhB->prepare($upd_stmt);
$upd_rslt->execute();
}
}
}
elsif ($VARFTP_host eq "localhost" && $location_search)
{
# Attempt to locate the file locally
$filepath="";
$nodatepath=$recording_dir."/".$filename."*";
if ($DB) {print " --- FILE NOT FOUND, SEARCHING LOCAL SERVER...\n";}
if ($nodatedir)
{
if ($DB) {print " ---- SEARCHING DATE DIRECTORIES...\n";}
if ($location=~/((\/20\d{2}-[01]\d-[0-3]\d\/$filename.*)|(\/20\d{2}\/[01]\d\/[0-3]\d\/$filename.*))/)
{
$filepath=$recording_dir.$1;
}
}
elsif ($YMDdatedir)
{
if ($DB) {print " ---- SEARCHING DEFAULT YYYY-MM-DD AND NO-DATE BASE DIRECTORIES...\n";}
if ($location=~/(\/20\d{2}-[01]\d-[0-3]\d\/$filename.*)/)
{
$filepath=$recording_dir.$1;
}
else
{
$filepath=$nodatepath;
}
}
else
{
if ($DB) {print " ---- SEARCHING SPLIT YYYY/MM/DD AND NO-DATE BASE DIRECTORIES...\n";}
if ($location=~/(\/20\d{2}\/[01]\d\/[0-3]\d\/$filename.*)/)
{
$filepath=$recording_dir.$1;
}
else
{
$filepath=$nodatepath;
}
}
if ($filepath)
{
@found_files = glob("$filepath");
if (scalar(@found_files)>0)
{
for ($i=0; $i<scalar(@found_files); $i++)
{
if ($DB) {print " ----- $found_files[$i] FOUND, DELETING...\n\n";}
$upd_stmt="update recording_log_deletion_queue set date_deleted=now() where recording_id='$recording_id'";
if ($T)
{
if ($DBX) {print "\n$upd_stmt\n";}
}
else
{
`rm $found_files[$i]`;
$upd_rslt=$dbhB->prepare($upd_stmt);
$upd_rslt->execute();
}
}
}
else
{
if ($DB) {print " ----- LOCATION LOOKUP $filepath FILES NOT FOUND\n\n";}
}
}
else
{
if ($DB) {print " ---- LOCATION DIRECTORY NOT FOUND\n\n";}
}
}
else
{
if ($DB) {print " --- $filepath NOT FOUND\n\n";}
}
}
}
$rslt->finish();
$dbhA->disconnect();
$dbhB->disconnect();
+86
View File
@@ -0,0 +1,86 @@
EUROPEAN UNION GDPR COMPLIANCE Started: 2018-03-02 Updated: 2018-03-02
!!!!! NOTE THIS SET OF FEATURES IS NOT ENABLED IN VICIDIAL BY DEFAULT !!!!!
This document describes the new EU GDPR(General Data Protection Regulation) features in Vicidial, as well as a brief overview of the GDPR regulations going into effect for organizations that interact with residents of the European Union as of May 25th, 2018.
The official EU GDPR website is located here:
https://www.eugdpr.org/
Good overview of GDPR rules and scope:
https://en.wikipedia.org/wiki/General_Data_Protection_Regulation
What is the GDPR?
The right of access (Article 15) is a data subject right that gives citizens the right to get access to their personal data and information about how this personal data is are being processed. A data controller must provide, upon request, an overview of the categories of data that are being processed (Article 15(1)(b)) as well as a copy of the actual data (Article 15(3)). Furthermore, the data controller has to inform the data subject on details about the processing such as the purposes are of the processing (Article 15(1)(a)), with whom the data is shared (Article 15(1)(c)), and how it acquired the data (Article 15(1)(g)).
A right to erasure (Article 17) provides that the data subject has the right to request erasure of personal data related to them on any one of a number of grounds, including noncompliance with Article 6.1 (lawfulness) that includes a case (f) if the legitimate interests of the controller is overridden by the interests or fundamental rights and freedoms of the data subject, which require protection of personal data.
And there are many other sections not directly affecting VICIdial software for affected organizations that you should read about if your organization is affected.
Who is affected by the GDPR?
The regulation applies if the data controller, an organisation that collects data from EU residents, or processor, an organisation that processes data on behalf of data controller like cloud service providers or the data subject (person) is based in the EU. The regulation also applies to organisations based outside the EU if they collect or process personal data of individuals located inside the EU. According to the European Commission, "personal data is any information relating to an individual, whether it relates to his or her private, professional or public life. It can be anything from a name, a home address, a photo, an email address, bank details, posts on social networking websites, medical information, or a computers IP address."
New VICIdial Settings -
Admin -> System Settings:
Enable GDPR-compliant Data Download Deletion -This setting if enabled will allow for the complete download and/or deletion of all customer data for a particular lead, in compliance with the General Data Protection Regulation (GDPR). Default is 0 for disabled. A setting of 1 will enable downloading data, and a setting of 2 will enable not just downloading, but also deletion of data, including any recordings.
Users -> Modify User:
GDPR-Compliant Export Delete Leads -This setting if enabled will allow for the complete download and/or deletion of all customer data for a particular lead, in compliance with the General Data Protection Regulation (GDPR). Default is 0 for disabled. A setting of 1 will enable downloading data, and a setting of 2 will enable not just downloading, but also deletion of data, including any recordings.
You are not allowed to set this user setting higher than the current system setting.
New VICIdial Web Administration Features -
On the Modify Lead Page(which you can access from either the "Lists -> Search for a Lead" page, or by clicking on a lead ID in the "User Stats" page):
At the bottom of the page, you may see these options:
GDPR compliance:
Click here to download GDPR-formatted data for this lead
Click here to review and purge customer data on lead
The "download" option will provide you with a ZIP file containing all lead and log data for the lead record in the system as well as all of the recordings that are available for the lead record.
The "review and purge" option will take you to a confirmation page where you can permanently delete the personal information that is stored in the system
New VICIdial back-end scripts -
AST_GDPR_audio_purge.pl:
# This script queries the recording log deletion queue and purges any audio recordings
# inserted into this table (done through the GDPR feature in the administration modify lead page)
# Set to purge nightly, or as often as desired
#
# 1 0 * * * /usr/share/astguiclient/AST_GDPR_audio_purge.pl --recording_dir=/var/spool/asterisk/monitorDONE
#
#
# FLAG FOR RECORDING DIRECTORY ON SERVER
# *** REQUIRED ***
# --recording_dir
#
# FLAG FOR NO DATE DIRECTORY ON SERVER
# --nodatedir
#
# FLAG FOR Y/M/D DATE DIRECTORY ON SERVER
# --YMDdatedir
#
# PERFORM ADDITIONAL DIRECTORY SEARCHES
# --location_search
#
+52 -2
View File
@@ -8,7 +8,7 @@
# just needs to enter the leadID and then they can view and modify the
# information in the record for that lead
#
# Copyright (C) 2017 Matt Florell <vicidial@gmail.com> LICENSE: AGPLv2
# Copyright (C) 2018 Matt Florell <vicidial@gmail.com> LICENSE: AGPLv2
#
# CHANGES
#
@@ -86,7 +86,8 @@
# 170826-0858 - Added link to turn on/off archived logs display
# 171001-1109 - Added in-browser audio control, if recording access control is disabled
# 171216-2058 - Added ability to modify all active scheduled callbacks, and view callbacks log
# 180212-2340 - Added GDPR compliance features
# 180212-2340 - Added basic GDPR compliance features
# 180302-1605 - Added complete GDPR compliance features
#
require("dbconnect_mysqli.php");
@@ -637,6 +638,32 @@ if ($enable_gdpr_download_deletion>0)
if (strlen($CSV_text)>0 && $gdpr_action=="download")
{
$rec_stmt="select start_time, location, filename from recording_log where lead_id='$lead_id' order by start_time asc";
$rec_rslt=mysql_to_mysqli($rec_stmt, $link);
$files_to_zip=array();
while ($rec_row=mysqli_fetch_row($rec_rslt)) {
$start_time=$rec_row[0];
$start_date=substr($start_time, 1, 10);
$location=$rec_row[1];
$filename=$rec_row[2];
preg_match("/$filename.*$/", $location, $matches);
$destination_filename=$matches[0];
set_time_limit(0);
$fp = fopen("/tmp/$destination_filename", 'w+');
$ch = curl_init(str_replace(" ","%20",$location));
curl_setopt($ch, CURLOPT_TIMEOUT, 50);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
array_push($files_to_zip, "$destination_filename");
}
$stmt="INSERT INTO vicidial_admin_log set event_date='$NOW_TIME', user='$PHP_AUTH_USER', ip_address='$ip', event_section='LEADS', event_type='EXPORT', record_id='$lead_id', event_code='GDPR EXPORT LEAD DATA', event_sql=\"$SQL_log\", event_notes='';";
if ($DB) {echo "|$stmt|\n";}
$rslt=mysql_to_mysqli($stmt, $link);
@@ -647,6 +674,27 @@ if ($enable_gdpr_download_deletion>0)
$CSV_text=preg_replace('/^\s+/', '', $CSV_text);
$CSV_text=preg_replace('/ +\"/', '"', $CSV_text);
$CSV_text=preg_replace('/\" +/', '"', $CSV_text);
array_push($files_to_zip, "$CSVfilename");
$fp = fopen("/tmp/$CSVfilename", 'w+');
fwrite($fp, $CSV_text);
fclose($fp);
$zipname = "$lead_id.zip";
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
foreach ($files_to_zip as $file) {
$tempfile="/tmp/$file";
$zip->addFile($tempfile);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
/*
// We'll be outputting a TXT file
header('Content-type: application/octet-stream');
@@ -659,6 +707,8 @@ if ($enable_gdpr_download_deletion>0)
flush();
echo "$CSV_text";
*/
exit;
}
}