feat: anonymize data for multiple email match field
This commit is contained in:
parent
58782a52b0
commit
5fea524ec5
3 changed files with 42 additions and 13 deletions
|
|
@ -229,13 +229,6 @@ before_migrate = ['frappe.patches.v11_0.sync_user_permission_doctype_before_migr
|
|||
|
||||
otp_methods = ['OTP App','Email','SMS']
|
||||
user_privacy_documents = [
|
||||
{
|
||||
'doctype': 'User',
|
||||
'match_field': 'email',
|
||||
'personal_fields': ['first_name', 'middle_name', 'last_name', 'full_name', 'username', 'birth_date', 'user_image', 'phone',
|
||||
'mobile_no', 'location', 'banner_image', 'interest', 'bio', 'email_signature', 'background_image'],
|
||||
'applies_to_website_user': 1
|
||||
},
|
||||
{
|
||||
'doctype': 'File',
|
||||
'match_field': 'attached_to_name',
|
||||
|
|
@ -263,6 +256,12 @@ user_privacy_documents = [
|
|||
'match_field': 'email_id',
|
||||
'personal_fields': ['first_name', 'last_name', 'phone', 'mobile_no'],
|
||||
},
|
||||
{
|
||||
'doctype': 'Address',
|
||||
'match_field': 'email_id',
|
||||
'personal_fields': ['address_title', 'address_line1', 'address_line2', 'city', 'county', 'state', 'pincode',
|
||||
'phone', 'fax'],
|
||||
},
|
||||
{
|
||||
'doctype': 'Communication',
|
||||
'match_field': 'sender',
|
||||
|
|
@ -272,5 +271,12 @@ user_privacy_documents = [
|
|||
'doctype': 'Communication',
|
||||
'match_field': 'recipients',
|
||||
},
|
||||
{
|
||||
'doctype': 'User',
|
||||
'match_field': 'name',
|
||||
'personal_fields': ['email', 'username', 'first_name', 'middle_name', 'last_name', 'full_name', 'birth_date',
|
||||
'user_image', 'phone', 'mobile_no', 'location', 'banner_image', 'interest', 'bio', 'email_signature', 'background_image'],
|
||||
'applies_to_website_user': 1
|
||||
},
|
||||
|
||||
]
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
import re
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.verified_command import get_signed_params, verify_request
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ class PersonalDataDeletionRequest(Document):
|
|||
header=[_("Confirm Deletion of Data"), "green"])
|
||||
|
||||
def anonymize_data(self):
|
||||
if 'System Manager' not in frappe.get_roles(frappe.session.user) and self.status != 'Pending Approval':
|
||||
if not ('System Manager' in frappe.get_roles(frappe.session.user) and self.status == 'Pending Approval'):
|
||||
frappe.throw(_("You are not authorized to complete this action."))
|
||||
|
||||
privacy_docs = frappe.get_hooks("user_privacy_documents")
|
||||
|
|
@ -37,6 +38,8 @@ class PersonalDataDeletionRequest(Document):
|
|||
'Int': 0,
|
||||
'Code': 'http://xxxxx'}
|
||||
|
||||
regex = re.compile(r"(?<!\.)\b{0}\b(?!\.)".format(re.escape(self.email)))
|
||||
|
||||
for ref_doc in privacy_docs:
|
||||
meta = frappe.get_meta(ref_doc['doctype'])
|
||||
personal_fields = ref_doc.get('personal_fields', [])
|
||||
|
|
@ -44,14 +47,32 @@ class PersonalDataDeletionRequest(Document):
|
|||
if ref_doc.get('applies_to_website_user') and 'Guest' not in frappe.get_roles(self.email):
|
||||
continue
|
||||
|
||||
anonymize_value = ''
|
||||
anonymize_fields = ''
|
||||
for field in personal_fields:
|
||||
anonymize_value += ', `{0}`= \'{1}\''.format(field, anonymize_value_map.get(meta.get_field(field).fieldtype, str(field)))
|
||||
field_details = meta.get_field(field)
|
||||
field_value = anonymize_value_map.get(field_details.fieldtype, str(field)) if not field_details.unique else self.name
|
||||
anonymize_fields += ', `{0}`= \'{1}\''.format(field, field_value)
|
||||
|
||||
if (meta.get_field(ref_doc['match_field']) or {}).get('fieldtype') == 'Code':
|
||||
self.anonymize_multi_email_record(ref_doc, anonymize_fields, regex)
|
||||
|
||||
else:
|
||||
frappe.db.sql("""UPDATE `tab{0}`
|
||||
SET `{1}` = '{2}' {3}
|
||||
WHERE `{1}` = '{4}' """.format(ref_doc['doctype'], ref_doc['match_field'], self.name,#nosec
|
||||
anonymize_fields, self.email))
|
||||
|
||||
def anonymize_multi_email_record(self, ref_doc, anonymize_fields, regex):
|
||||
docs = frappe.get_all(ref_doc['doctype'], {ref_doc['match_field']:('like', '%'+self.email+'%')}, ['name', ref_doc['match_field']])
|
||||
for d in docs:
|
||||
if not re.search(regex, d[ref_doc['match_field']]):
|
||||
continue
|
||||
|
||||
anonymize_match_value = ', '.join(map(lambda x: self.name if re.search(regex, x) else x, d[ref_doc['match_field']].split()))
|
||||
frappe.db.sql("""UPDATE `tab{0}`
|
||||
SET `{1}` = '{2}' {3}
|
||||
WHERE `{1}` = '{4}' """.format(ref_doc['doctype'], ref_doc['match_field'], self.name,#nosec
|
||||
anonymize_value, self.email))
|
||||
WHERE `name` = '{4}' """.format(ref_doc['doctype'], ref_doc['match_field'], anonymize_match_value,#nosec
|
||||
anonymize_fields, d['name']))
|
||||
|
||||
def remove_unverified_record():
|
||||
frappe.db.sql("""DELETE FROM `tabPersonal Data Deletion Request` WHERE `status` = 'Pending Verification' and `creation` < (NOW() - INTERVAL '7' DAY)""")
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ class TestPersonalDataDeletionRequest(unittest.TestCase):
|
|||
self.assertTrue("Subject: Confirm Deletion of Data" in email_queue[0].message)
|
||||
|
||||
def test_anonymized_data(self):
|
||||
self.delete_request.status = 'Pending Approval'
|
||||
self.delete_request.save()
|
||||
PersonalDataDeletionRequest.anonymize_data(self.delete_request)
|
||||
deleted_user = frappe.get_all('Contact',
|
||||
{'email_id': self.delete_request.name},
|
||||
|
|
@ -45,4 +47,4 @@ class TestPersonalDataDeletionRequest(unittest.TestCase):
|
|||
self.status = 'Pending Verification'
|
||||
self.delete_request.save()
|
||||
remove_unverified_record()
|
||||
self.assertFalse(frappe.db.exists("Personal Data Deletion Request", self.delete_request.name))
|
||||
self.assertFalse(frappe.db.exists("Personal Data Deletion Request", self.delete_request.name))
|
||||
Loading…
Add table
Reference in a new issue