refactor: change approach of masking fields
This commit is contained in:
parent
c0aa39ee9a
commit
c2544f9096
8 changed files with 56 additions and 109 deletions
|
|
@ -52,24 +52,12 @@ def get_meta(doctype, cached=True) -> "FormMeta":
|
|||
|
||||
|
||||
def mask_protected_fields(meta):
|
||||
if (
|
||||
frappe.flags.in_patch
|
||||
or frappe.flags.in_install
|
||||
or frappe.flags.in_migrate
|
||||
or frappe.flags.in_setup_wizard
|
||||
):
|
||||
return meta
|
||||
|
||||
for df in meta.fields:
|
||||
if df.mask and not meta.has_permlevel_access_to(
|
||||
fieldname=df.fieldname, df=df, permission_type="mask"
|
||||
):
|
||||
# store orignal fieldtype and change fieldtype to Data
|
||||
df.read_only = 1
|
||||
df.mask_readonly = 1
|
||||
df.set("old_fieldtype", df.get("old_fieldtype") or df.fieldtype)
|
||||
if df.fieldtype != "Data":
|
||||
df.fieldtype = "Data"
|
||||
return meta
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -376,8 +376,7 @@ def export_query():
|
|||
form_params = get_form_params()
|
||||
form_params["limit_page_length"] = None
|
||||
|
||||
# remove as_list param because key is needed for data masking
|
||||
# form_params["as_list"] = True
|
||||
form_params["as_list"] = True
|
||||
doctype = form_params.pop("doctype")
|
||||
if isinstance(form_params["fields"], list):
|
||||
form_params["fields"].append("owner")
|
||||
|
|
@ -421,10 +420,6 @@ def export_query():
|
|||
data = [[_("Sr"), *labels]]
|
||||
processed_data = []
|
||||
|
||||
# convert ret to a list of lists if it is a list of dicts
|
||||
if isinstance(ret, list) and ret and isinstance(ret[0], dict):
|
||||
ret = [[value for value in row.values()] for row in ret]
|
||||
|
||||
if frappe.local.lang == "en" or not translate_values:
|
||||
data.extend([i + 1, *list(row)] for i, row in enumerate(ret))
|
||||
elif translate_values:
|
||||
|
|
|
|||
|
|
@ -231,24 +231,36 @@ class DatabaseQuery:
|
|||
if not masked_fields:
|
||||
return result
|
||||
|
||||
for row in result:
|
||||
for field in masked_fields:
|
||||
if field.fieldname in row:
|
||||
val = row[field.fieldname]
|
||||
if not val:
|
||||
continue
|
||||
if self.as_list:
|
||||
masked_result = []
|
||||
field_index_map = {}
|
||||
for idx, field in enumerate(self.fields):
|
||||
# handle aliases (e.g. `tabSI`.`posting_date` as posting_date)
|
||||
if " as " in field.lower():
|
||||
alias = field.split(" as ")[1].strip(" '")
|
||||
field_index_map[alias] = idx
|
||||
else:
|
||||
# extract last part after `.`
|
||||
col = field.split(".")[-1].strip("`")
|
||||
field_index_map[col] = idx
|
||||
# if as_list then we don't have field names in the result so we need to mask by position
|
||||
for row in result:
|
||||
row = list(row) # convert tuple to list mutable
|
||||
for field in masked_fields:
|
||||
if field.fieldname in field_index_map:
|
||||
idx = field_index_map[field.fieldname]
|
||||
val = row[idx]
|
||||
row[idx] = mask_field_value(field, val)
|
||||
|
||||
masked_result.append(tuple(row)) # convert back to tuple
|
||||
result = masked_result
|
||||
else:
|
||||
for row in result:
|
||||
for field in masked_fields:
|
||||
if field.fieldname in row:
|
||||
val = row[field.fieldname]
|
||||
row[field.fieldname] = mask_field_value(field, val)
|
||||
|
||||
if field.old_fieldtype == "Data" and field.options == "Phone":
|
||||
row[field.fieldname] = val[:3] + "XXXXXX"
|
||||
elif field.old_fieldtype == "Data" and field.options == "Email":
|
||||
email = val.split("@")
|
||||
row[field.fieldname] = "XXXXXX@" + email[1]
|
||||
elif field.old_fieldtype == "Date":
|
||||
row[field.fieldname] = "XX-XX-XXXX"
|
||||
elif field.old_fieldtype == "Time":
|
||||
row[field.fieldname] = "XX:XX"
|
||||
else:
|
||||
row[field.fieldname] = "XXXXXXXX"
|
||||
return result
|
||||
|
||||
def get_masked_fields(self):
|
||||
|
|
@ -660,15 +672,6 @@ from {tables}
|
|||
)
|
||||
)
|
||||
|
||||
# get_permitted_field = get_permitted_fields(
|
||||
# doctype=self.doctype,
|
||||
# parenttype=self.parent_doctype,
|
||||
# permission_type=self.permission_map.get(self.doctype),
|
||||
# ignore_virtual=True,
|
||||
# )
|
||||
|
||||
print(self.doctype, self.permission_map.get(self.doctype), "permitted_fields \n\n\n\n")
|
||||
# print(get_permitted_field, "get_permitted_field \n\n\n\n")
|
||||
permitted_child_table_fields = {}
|
||||
|
||||
for i, field in enumerate(self.fields):
|
||||
|
|
@ -1239,6 +1242,23 @@ from {tables}
|
|||
update_user_settings(self.doctype, user_settings)
|
||||
|
||||
|
||||
def mask_field_value(field, val):
|
||||
if not val:
|
||||
return val
|
||||
|
||||
if field.fieldtype == "Data" and field.options == "Phone":
|
||||
return val[:3] + "XXXXXX"
|
||||
elif field.fieldtype == "Data" and field.options == "Email":
|
||||
email = val.split("@")
|
||||
return "XXXXXX@" + email[1] if len(email) > 1 else "XXXXXX"
|
||||
elif field.fieldtype == "Date":
|
||||
return "XX-XX-XXXX"
|
||||
elif field.fieldtype == "Time":
|
||||
return "XX:XX"
|
||||
else:
|
||||
return "XXXXXXXX"
|
||||
|
||||
|
||||
def cast_name(column: str) -> str:
|
||||
"""Casts name field to varchar for postgres
|
||||
|
||||
|
|
|
|||
|
|
@ -273,70 +273,16 @@ class Document(BaseDocument):
|
|||
|
||||
return self
|
||||
|
||||
def mask_field_value(self, field):
|
||||
# TODO: use this method to mask value and remove other code
|
||||
already_masked = False
|
||||
|
||||
if field.fieldtype == "Data" and field.options == "Phone":
|
||||
already_masked = True
|
||||
self.set(field.fieldname, self.get(field.fieldname)[0:3] + "XXXXXXX")
|
||||
|
||||
if field.fieldtype == "Data" and field.options == "Email":
|
||||
already_masked = True
|
||||
email = self.get(field.fieldname)
|
||||
if email:
|
||||
email = email.split("@")
|
||||
self.set(field.fieldname, "XXXXXXXX" + "@" + email[1])
|
||||
|
||||
if field.fieldtype == "Date":
|
||||
already_masked = True
|
||||
date = self.get(field.fieldname)
|
||||
if date:
|
||||
self.set(field.fieldname, "XX-XX-XXXX")
|
||||
|
||||
if field.fieldtype == "Time":
|
||||
already_masked = True
|
||||
date = self.get(field.fieldname)
|
||||
if date:
|
||||
self.set(field.fieldname, "XX:XX")
|
||||
|
||||
if not already_masked:
|
||||
self.set(field.fieldname, "XXXXXXXX")
|
||||
|
||||
def mask_fields(self):
|
||||
from frappe.model.db_query import mask_field_value
|
||||
|
||||
mask_fields = frappe.get_meta(self.doctype).get_masked_fields()
|
||||
|
||||
if not mask_fields:
|
||||
return
|
||||
for field in mask_fields:
|
||||
already_masked = False
|
||||
|
||||
# if field type is Data and option is Phone the mask all value except last 3
|
||||
if field.old_fieldtype == "Data" and field.options == "Phone":
|
||||
already_masked = True
|
||||
self.set(field.fieldname, self.get(field.fieldname)[0:3] + "XXXXXXX")
|
||||
|
||||
if field.old_fieldtype == "Data" and field.options == "Email":
|
||||
already_masked = True
|
||||
email = self.get(field.fieldname)
|
||||
if email:
|
||||
email = email.split("@")
|
||||
self.set(field.fieldname, "XXXXXXXX" + "@" + email[1])
|
||||
|
||||
if field.old_fieldtype == "Date":
|
||||
already_masked = True
|
||||
date = self.get(field.fieldname)
|
||||
if date:
|
||||
self.set(field.fieldname, "XX-XX-XXXX")
|
||||
|
||||
if field.old_fieldtype == "Time":
|
||||
already_masked = True
|
||||
date = self.get(field.fieldname)
|
||||
if date:
|
||||
self.set(field.fieldname, "XX:XXXX")
|
||||
|
||||
if not already_masked:
|
||||
self.set(field.fieldname, "XXXXXXXX")
|
||||
val = self.get(field.fieldname)
|
||||
self.set(field.fieldname, mask_field_value(field, val))
|
||||
|
||||
def load_children_from_db(self):
|
||||
is_doctype = self.doctype == "DocType"
|
||||
|
|
@ -981,7 +927,6 @@ class Document(BaseDocument):
|
|||
return
|
||||
|
||||
# check for child tables
|
||||
print(high_permlevel_fields, "high_permlevel_fields \n\n\n")
|
||||
for df in self.meta.get_table_fields():
|
||||
high_permlevel_fields = frappe.get_meta(df.options).get_high_permlevel_fields()
|
||||
if high_permlevel_fields:
|
||||
|
|
|
|||
|
|
@ -88,14 +88,13 @@ def get_meta(doctype: "str | DocType", cached: bool = True) -> "_Meta":
|
|||
|
||||
meta = Meta(doctype)
|
||||
|
||||
key = f"doctype_meta::{meta.name}"
|
||||
frappe.client_cache.set_value(key, meta)
|
||||
|
||||
if meta.name not in meta.special_doctypes:
|
||||
from frappe.desk.form.meta import mask_protected_fields
|
||||
|
||||
meta = mask_protected_fields(meta)
|
||||
|
||||
key = f"doctype_meta::{meta.name}"
|
||||
frappe.client_cache.set_value(key, meta)
|
||||
return meta
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ frappe.ui.form.Control = class BaseControl {
|
|||
make() {
|
||||
this.make_wrapper();
|
||||
this.$wrapper
|
||||
.attr("data-fieldtype", this.df?.old_fieldtype || this.df.fieldtype)
|
||||
.attr("data-fieldtype", this.df.fieldtype)
|
||||
.attr("data-fieldname", this.df.fieldname);
|
||||
this.wrapper = this.$wrapper.get(0);
|
||||
this.wrapper.fieldobj = this; // reference for event handlers
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ frappe.form.get_formatter = function (fieldtype) {
|
|||
};
|
||||
|
||||
frappe.format = function (value, df, options, doc) {
|
||||
if (!df) df = { fieldtype: "Data" };
|
||||
if (!df || df?.mask_readonly) df = { fieldtype: "Data" };
|
||||
if (df.fieldname == "_user_tags") df = { ...df, fieldtype: "Tag" };
|
||||
var fieldtype = df.fieldtype || "Data";
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ frappe.ui.form.Layout = class Layout {
|
|||
init_field(df, parent, render = false) {
|
||||
if (df.mask && df.mask_readonly) {
|
||||
if (df.fieldtype !== "Data") {
|
||||
df.original_fieldtype = df.fieldtype;
|
||||
df.read_only = 1;
|
||||
df.fieldtype = "Data";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue