Skip to content

Commit

Permalink
feat: DMARC Viewer report
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r committed Nov 22, 2024
1 parent 2c2e73c commit 8e65b39
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 15 deletions.
13 changes: 11 additions & 2 deletions mail_client/mail_client/doctype/dmarc_report/dmarc_report.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"actions": [],
"autoname": "hash",
"creation": "2024-11-19 11:38:22.846485",
"doctype": "DocType",
"engine": "InnoDB",
Expand All @@ -8,6 +9,7 @@
"domain_name",
"policy_published",
"column_break_kyq2",
"report_id",
"organization",
"email",
"extra_contact_info",
Expand Down Expand Up @@ -107,16 +109,23 @@
"no_copy": 1,
"options": "DMARC Report Detail",
"read_only": 1
},
{
"fieldname": "report_id",
"fieldtype": "Data",
"label": "Report ID",
"length": 255,
"unique": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-11-19 20:02:12.652268",
"modified": "2024-11-21 18:16:14.062611",
"modified_by": "Administrator",
"module": "Mail Client",
"name": "DMARC Report",
"naming_rule": "Set by user",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [
{
Expand Down
17 changes: 8 additions & 9 deletions mail_client/mail_client/doctype/dmarc_report/dmarc_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
import xmltodict
from frappe.model.document import Document
from frappe.utils import cint, convert_utc_to_system_timezone, get_datetime_str
from uuid_utils import uuid7


class DMARCReport(Document):
def autoname(self) -> None:
self.name = self.report_id or str(uuid7())
pass


def create_dmarc_report(xml_content: str) -> "DMARCReport":
Expand Down Expand Up @@ -66,8 +64,8 @@ def create_dmarc_report(xml_content: str) -> "DMARCReport":
source_ip = row["source_ip"]
count = row["count"]
disposition = policy_evaluated["disposition"]
dkim_result = policy_evaluated["dkim"]
spf_result = policy_evaluated["spf"]
dkim_result = policy_evaluated["dkim"].upper()
spf_result = policy_evaluated["spf"].upper()
header_from = identifiers["header_from"]

results = []
Expand All @@ -76,7 +74,8 @@ def create_dmarc_report(xml_content: str) -> "DMARCReport":
auth_result = [auth_result]

for result in auth_result:
result["auth_type"] = auth_type
result["auth_type"] = auth_type.upper()
result["result"] = result["result"].upper()
results.append(result)

doc.append(
Expand All @@ -95,11 +94,11 @@ def create_dmarc_report(xml_content: str) -> "DMARCReport":
doc.flags.ignore_links = True

try:
doc.insert(ignore_permissions=True)
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
return doc
except frappe.DuplicateEntryError:
except frappe.UniqueValidationError:
frappe.log_error(
title="Duplicate DMARC Report",
message=frappe.get_traceback(with_context=True),
)
return frappe.get_doc("DMARC Report", doc.name)
return frappe.get_doc("DMARC Report", {"report_id": doc.report_id})
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "DKIM Result",
"options": "\npass\nfail"
"options": "\nPASS\nFAIL"
},
{
"fieldname": "spf_result",
"fieldtype": "Select",
"in_list_view": 1,
"label": "SPF Result",
"options": "\npass\nfail"
"options": "\nPASS\nFAIL"
},
{
"fieldname": "header_from",
Expand Down Expand Up @@ -86,7 +86,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-11-19 13:34:47.458647",
"modified": "2024-11-21 20:57:24.194305",
"modified_by": "Administrator",
"module": "Mail Client",
"name": "DMARC Report Detail",
Expand Down
Empty file.
57 changes: 57 additions & 0 deletions mail_client/mail_client/report/dmarc_viewer/dmarc_viewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.query_reports["DMARC Viewer"] = {
formatter(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);

if (["spf_result", "dkim_result", "result"].includes(column.fieldname)) {
if (data[column.fieldname] == "PASS") {
value = "<span style='color:green'>" + value + "</span>";
} else {
value = "<span style='color:red'>" + value + "</span>";
}
}

return value;
},

filters: [
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.add_days(frappe.datetime.get_today(), -7),
reqd: 1,
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
reqd: 1,
},
{
fieldname: "name",
label: __("DMARC Report"),
fieldtype: "Link",
options: "DMARC Report",
},
{
fieldname: "domain_name",
label: __("Domain Name"),
fieldtype: "Link",
options: "Mail Domain",
},
{
fieldname: "organization",
label: __("Organization"),
fieldtype: "Data",
},
{
fieldname: "report_id",
label: __("Report ID"),
fieldtype: "Data",
},
],
};
27 changes: 27 additions & 0 deletions mail_client/mail_client/report/dmarc_viewer/dmarc_viewer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2024-11-21 17:31:42.807210",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2024-11-21 17:31:42.807210",
"modified_by": "Administrator",
"module": "Mail Client",
"name": "DMARC Viewer",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "DMARC Report",
"report_name": "DMARC Viewer",
"report_type": "Script Report",
"roles": [
{
"role": "System Manager"
}
],
"timeout": 0
}
190 changes: 190 additions & 0 deletions mail_client/mail_client/report/dmarc_viewer/dmarc_viewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

import json

import frappe
from frappe import _
from frappe.query_builder import Order
from frappe.query_builder.functions import Date


def execute(filters: dict | None = None) -> tuple:
columns = get_columns()
data = get_data(filters)

return columns, data


def get_columns() -> list[dict]:
return [
{
"label": _("Name"),
"fieldname": "name",
"fieldtype": "Link",
"options": "DMARC Report",
"width": 120,
},
{
"label": _("From Date"),
"fieldname": "from_date",
"fieldtype": "Datetime",
"width": 180,
},
{
"label": _("To Date"),
"fieldname": "to_date",
"fieldtype": "Datetime",
"width": 180,
},
{
"label": _("Domain Name"),
"fieldname": "domain_name",
"fieldtype": "Link",
"options": "Mail Domain",
"width": 150,
},
{
"label": _("Organization"),
"fieldname": "organization",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Report ID"),
"fieldname": "report_id",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Source IP"),
"fieldname": "source_ip",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Count"),
"fieldname": "count",
"fieldtype": "Int",
"width": 70,
},
{
"label": _("Disposition"),
"fieldname": "disposition",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Header From"),
"fieldname": "header_from",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("SPF Result"),
"fieldname": "spf_result",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("DKIM Result"),
"fieldname": "dkim_result",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Auth Type"),
"fieldname": "auth_type",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Selector / Scope"),
"fieldname": "selector_or_scope",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Domain"),
"fieldname": "domain",
"fieldtype": "Data",
"width": 150,
},
{
"label": _("Result"),
"fieldname": "result",
"fieldtype": "Data",
"width": 150,
},
]


def get_data(filters: dict | None = None) -> list[list]:
filters = filters or {}

DR = frappe.qb.DocType("DMARC Report")

query = (
frappe.qb.from_(DR)
.select(
DR.name,
DR.from_date,
DR.to_date,
DR.domain_name,
DR.organization,
DR.report_id,
)
.orderby(DR.from_date, order=Order.desc)
)

if not filters.get("name"):
query = query.where(
(Date(DR.from_date) >= Date(filters.get("from_date")))
& (Date(DR.to_date) <= Date(filters.get("to_date")))
)

for field in [
"name",
"domain_name",
"organization",
"report_id",
]:
if filters.get(field):
query = query.where(DR[field] == filters.get(field))

data = query.run(as_dict=True)

formated_data = []
for d in data:
records = frappe.db.get_all(
"DMARC Report Detail",
filters={"parenttype": "DMARC Report", "parent": d.name},
fields=[
"source_ip",
"count",
"disposition",
"header_from",
"spf_result",
"dkim_result",
"auth_results",
],
)

d["indent"] = 0
formated_data.append(d)
for record in records:
record["indent"] = 1
formated_data.append(record)

auth_results = json.loads(record.auth_results)
for auth_result in auth_results:
auth_result["indent"] = 2

if auth_result["auth_type"] == "DKIM":
auth_result["selector_or_scope"] = auth_result.get("selector")
else:
auth_result["selector_or_scope"] = auth_result.get("scope")

formated_data.append(auth_result)

return formated_data
Loading

0 comments on commit 8e65b39

Please sign in to comment.