diff --git a/classes/check/dnsmx.php b/classes/check/dnsmx.php index 9f2c0b8..7d13dca 100644 --- a/classes/check/dnsmx.php +++ b/classes/check/dnsmx.php @@ -78,9 +78,7 @@ public function get_result(): result { $status = result::WARNING; $summary = "MX DNS record missing"; } else { - $allmxdomains = join('
', array_map(function ($x) { - return $x['target'] . ' (' . $x['pri'] . ')'; - }, $mxdomains)); + $allmxdomains = $dns->format_mx_records($mxdomains); $details .= "

MX record found on domain $noreplydomain pointing to
$allmxdomains

"; $status = result::OK; $summary = "MX record points to " . $mxdomains[0]['target']; diff --git a/classes/check/dnspostmastertools.php b/classes/check/dnspostmastertools.php index 784d166..399e3db 100644 --- a/classes/check/dnspostmastertools.php +++ b/classes/check/dnspostmastertools.php @@ -54,21 +54,14 @@ public function get_action_link(): ?\action_link { * @return result */ public function get_result(): result { - global $DB, $CFG; + global $OUTPUT; - $url = new \moodle_url($CFG->wwwroot); - $domain = $url->get_host(); - - $details = ''; - $details .= ''; $status = result::INFO; $summary = ''; $dns = new dns_util(); $noreply = $dns->get_noreply(); - $details .= "

No reply email: $noreply

"; - $noreplydomain = $dns->get_noreply_domain(); // Later intend to support other email providers. @@ -76,28 +69,67 @@ public function get_result(): result { $vendornames = join(', ', $vendors); $summary = "Post master tools setup for $vendornames "; - $status = result::INFO; + // Check the most common user domains. + $userdomains = $dns->get_user_domains(10); + $userdomaininfo = []; + foreach ($userdomains as $domain) { + $mxrecords = $dns->get_mx_record($domain->domain); + $allmxdomains = $dns->format_mx_records($mxrecords); + $domainvendors = array_filter($vendors, function($vendor) use ($mxrecords) { + foreach ($mxrecords as $mxrecord) { + if (strpos($mxrecord['target'], $vendor) !== false) { + return true; + } + } + }); + $userdomaininfo[] = (object) [ + 'domain' => '@' . $domain->domain, + 'count' => $domain->count, + 'mxrecords' => $allmxdomains, + 'vendors' => implode(',', $domainvendors), + ]; + } + $status = result::INFO; + $vendorinfo = []; foreach ($vendors as $vendor) { $token = get_config('tool_emailutils', 'postmaster' . $vendor . 'token'); $record = $dns->get_matching_dns_record($noreplydomain, $token); + $usevendor = !empty(array_filter($userdomaininfo, function($row) use ($vendor) { + return strpos($row->vendors, $vendor) !== false; + })); - if (empty($token)) { + if (empty($token) && !$usevendor) { + $summary = "Post master tools not required for $vendor"; + $status = result::NA; + $confirmed = 'N/A'; + } else if (empty($token)) { $summary = "Post master tools not setup with $vendor"; - $status = result::WARNING; + $status = result::INFO; $confirmed = 'N/A'; } else if (empty($record)) { $confirmed = 'No'; $details .= "

$token was not found in TXT records

"; - $status = result::ERROR; + $status = result::WARNING; $summary = "Post master tools not verified with $vendor"; } else { $confirmed = 'Yes'; $status = result::OK; } - $details .= ""; + + $vendorinfo = (object) [ + 'vendor' => $vendor, + 'token' => $token, + 'confirmed' => $confirmed, + 'url' => '', + ]; } - $details .= '
VendorTokenConfirmedUrl
$vendor$token$confirmed
'; + + $details = $OUTPUT->render_from_template('tool_emailutils/postmaster', [ + 'noreply' => $noreply, + 'userdomains' => $userdomaininfo, + 'vendorinfo' => $vendorinfo, + ]); return new result($status, $summary, $details); } diff --git a/classes/dns_util.php b/classes/dns_util.php index 09596c0..9d844b3 100644 --- a/classes/dns_util.php +++ b/classes/dns_util.php @@ -96,6 +96,23 @@ public function get_subdomains($domain) { return rtrim(strstr($domain, $primarydomain, true), '.'); } + /** + * Gets the most common user email domains from the database. + * @param int $number limits the number of domains + * @return array + */ + public function get_user_domains(int $number = 0): array { + global $DB; + + $domainsql = $DB->sql_substr('LOWER(email)', $DB->sql_position("'@'", 'email') . ' + 1'); + $sql = "SELECT $domainsql AS domain, count(*) AS count + FROM {user} + WHERE email LIKE '%@%.%' AND suspended = 0 AND deleted = 0 + GROUP BY $domainsql + ORDER BY count DESC"; + return $DB->get_records_sql($sql, null, 0, $number); + } + /** * Get spf txt record contents * @param string $domain specify a different domain @@ -235,6 +252,17 @@ public function get_mx_record($domain) { return $records; } + /** + * Formats MX records to display in checks + * @param array $mxrecords + * @return string + */ + public function format_mx_records(array $mxrecords): string { + return join('
', array_map(function ($x) { + return $x['target'] . ' (' . $x['pri'] . ')'; + }, $mxrecords)); + } + /** * Get matching record contents * @param string $domain domain to check diff --git a/lang/en/tool_emailutils.php b/lang/en/tool_emailutils.php index 0d5fc19..ba21ac9 100644 --- a/lang/en/tool_emailutils.php +++ b/lang/en/tool_emailutils.php @@ -53,6 +53,7 @@ $string['dnsspfinclude_help'] = '

This is an SPF include domain which is expected to be present in the record. For example if this was set to spf.acme.org then the SPF security check would pass if the SPF record was v=spf1 include:spf.acme.org -all.

The * char can be used as a wildcard eg *acme.org would also match.

'; +$string['domain'] = 'Domain'; $string['domaindefaultnoreply'] = 'Default noreply'; $string['enabled'] = 'Enabled'; $string['enabled_help'] = 'Allow the plugin to process incoming messages'; @@ -61,6 +62,8 @@ $string['header_help'] = 'HTTP Basic Auth Header'; $string['incorrect_access'] = 'Incorrect access detected. For use only by AWS SNS.'; $string['list'] = 'Complaints List'; +$string['noreplyemail'] = 'No reply email: {$a}'; +$string['mxrecords'] = 'MX records'; $string['mxtoolbox'] = 'MXtoolbox links'; $string['not_implemented'] = 'Not implemented yet. Search the user report for emails ending with ".b.invalid" and ".c.invalid".'; $string['password'] = 'Password'; @@ -92,4 +95,6 @@ $string['settings'] = 'AWS SES settings'; $string['username'] = 'Username'; $string['username_help'] = 'HTTP Basic Auth Username'; +$string['userdomains'] = 'Most common user email domains'; +$string['vendor'] = 'Vendor'; $string['selectornotblank'] = 'Selector cannot be empty'; diff --git a/templates/postmaster.mustache b/templates/postmaster.mustache new file mode 100644 index 0000000..f7cb45e --- /dev/null +++ b/templates/postmaster.mustache @@ -0,0 +1,83 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_emailutils/postmaster + + Example context (json): + { + "noreply": "noreply@example.com", + "userdomains": [ + { + "domain": "@gmail.com", + "count": 100, + "mxrecords": "string", + "vendor": "google", + } + ], + "vendorinfo": [ + { + "vendors": "google", + "token": "token", + "confirmed": "N/A", + "url": "https://example.com", + } + ], + } +}} +

{{#str}} noreplyemail, tool_emailutils, {{ noreply }} {{/str}}

+ + + + + + + + + + + {{#vendorinfo}} + + + + + + + {{/vendorinfo}} + +
{{#str}} vendor, tool_emailutils {{/str}}{{#str}} token, core_webservice {{/str}}{{#str}} confirmed, core_admin {{/str}}{{#str}} url {{/str}}
{{ vendor }}{{ token }}{{ confirmed }}{{ url }}
+ +
{{#str}} userdomains, tool_emailutils {{/str}}
+ + + + + + + + + + + {{#userdomains}} + + + + + + {{/userdomains}} + +
{{#str}} domain, tool_emailutils {{/str}}{{#str}} users {{/str}}{{#str}} mxrecords, tool_emailutils {{/str}}{{#str}} vendor, tool_emailutils {{/str}}
{{ domain }}{{ count }}{{{ mxrecords }}} + {{ vendors }}
diff --git a/version.php b/version.php index 6389b83..be6894c 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024011502; -$plugin->release = 2024011502; +$plugin->version = 2024101700; +$plugin->release = 2024101700; $plugin->requires = 2020061500; $plugin->component = 'tool_emailutils'; $plugin->dependencies = ['local_aws' => 2020061500];