type = submodule {
options = {
mysql = mkMysqlOptions "DMARC" {};
+ anonymous_key = mkOption { type = str; description = "Anonymous hashing key"; };
};
};
};
$dbuser = "${env.mysql.user}";
$dbpass = "${env.mysql.password}";
$dbport = "${env.mysql.port}";
+ $anonymous_key = "${env.anonymous_key}";
?>
'';
}];
AllowOverride None
Options +FollowSymlinks
+
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+ Use LDAPConnect
Require all granted
+ Require ldap-attribute uid=immae
</Directory>
'';
};
die(json_encode($message));
}
+$anonymous = isset($_GET['anonymous']) && $_GET['anonymous'];
+function maybe_anonymize($string, $long = false) {
+ global $anonymous_key;
+ global $anonymous;
+ if ($anonymous) {
+ if ($long) {
+ return md5($anonymous_key . ":" . $string);
+ } else {
+ return substr(md5($anonymous_key . ":" . $string), 0, 6);
+ }
+ } else {
+ return $string;
+ }
+}
+
+if (!$anonymous && (!isset($_SERVER['HTTP_AUTHORIZATION']) || $_SERVER['HTTP_AUTHORIZATION'] === "")) {
+ header('WWW-Authenticate: Basic realm="Immae"');
+ header('HTTP/1.0 401 Unauthorized');
+ echo "You need to be authenticated to access private information";
+ exit;
+}
+
if ($mysqli->connect_errno) {
error_die($mysqli->connect_error, $mysqli->connect_errno);
}
$query = $mysqli->query("SELECT DISTINCT domain FROM `report` ORDER BY domain");
if ($mysqli->error) { error_die($mysqli->error, $mysqli->errno); }
while($row = $query->fetch_assoc()) {
- $response["domains"][] = $row['domain'];
+ $response["domains"][] = maybe_anonymize($row['domain']);
}
$response["orgs"] = array();
$query = $mysqli->query("SELECT DISTINCT org FROM `report` ORDER BY org");
if ($mysqli->error) { error_die($mysqli->error, $mysqli->errno); }
while($row = $query->fetch_assoc()) {
- $response["orgs"][] = $row['org'];
+ $response["orgs"][] = maybe_anonymize($row['org']);
}
$response["dates"] = array();
$query = $mysqli->query($sql);
if ($mysqli->error) { error_die($mysqli->error, $mysqli->errno); }
while($row = $query->fetch_assoc()) {
- unset($row["raw_xml"]);
+ $wanted_keys = array(
+ 'domain', 'org', 'reportid', 'mindate', 'maxdate', 'rcount', 'serial', 'policy_adkim', 'policy_aspf', 'policy_none', 'policy_sp', 'policy_pct', 'spfresult', 'dkimresult'
+ );
+ $row = array_intersect_key($row, array_fill_keys($wanted_keys, '1'));
+ $row["domain"] = maybe_anonymize($row["domain"]);
+ $row["org"] = maybe_anonymize($row["org"]);
+ $row["reportid"] = maybe_anonymize($row["reportid"], true);
$response["summaries"][] = $row;
}
} else {
$ip = "-";
$host = "-";
}
- $row['ip'] = $ip;
- $row['host'] = $host;
- unset($row['ip6']);
+ $wanted_keys = array(
+ 'ip', 'host', 'rcount', 'disposition', 'reason', 'dkimdomain', 'dkimresult', 'spfdomain', 'spfresult'
+ );
+ $row = array_intersect_key($row, array_fill_keys($wanted_keys, '1'));
+ $row['ip'] = maybe_anonymize($ip);
+ $row['host'] = maybe_anonymize($host);
+ $row['dkimdomain'] = maybe_anonymize($row['dkimdomain']);
+ $row['spfdomain'] = maybe_anonymize($row['spfdomain']);
$response["rptrecord"][] = $row;
}
}
el: '#app',
data: {
info: null,
- summaries: [],
+ summaries: null,
selectedSummary: null,
filterGreen: true,
filterDomain: null,
//filterDate: (new Date()).toISOString().substring(0, 7),
filterDate: null,
reverse: true,
+ anonymous: true,
},
created: async function () {
let that = this;
- try {
- this.info = await this.getInfo();
- this.summaries = this.info.summaries;
- } catch (error) {}
+ if ('anonymous' in localStorage) {
+ this.anonymous = JSON.parse(localStorage.anonymous);
+ }
+ this.fetchAll();
},
methods: {
+ fetchAll: async function() {
+ try {
+ this.info = await this.getInfo();
+ this.summaries = this.info.summaries;
+ } catch (error) {
+ this.info = null;
+ this.summaries = null;
+ }
+ },
+ toggleAnonymous: function() {
+ this.anonymous = !this.anonymous;
+ localStorage.anonymous = this.anonymous;
+ this.fetchAll();
+ },
filtered: function () {
let that = this;
let filtered = this.summaries.filter(function (summary) {
return mindate === this.filterDate || maxdate === this.filterDate;
},
printDate: function (date) {
- return (new Date(date)).toISOString();
+ return (new Date(date)).toISOString().replace("T", " ").replace(/\..*Z$/, " UTC");
},
getColor: function (element) {
if (element.dkimresult === "fail" && element.spfresult === "fail") {
}
},
getInfo: function (event) {
- return fetch('api.php').then(function (response) {
+ let anonymous = this.anonymous ? "anonymous=1" : "";
+ return fetch(`api.php?${anonymous}`).then(function (response) {
if (response.status != 200) { return; }
return response.text().then(function (body) {
return JSON.parse(body);
});
},
getDetails: function (serial) {
- return fetch(`api.php?serial=${serial}`).then(function (response) {
+ let anonymous = this.anonymous ? "&anonymous=1" : "";
+ return fetch(`api.php?serial=${serial}${anonymous}`).then(function (response) {
if (response.status != 200) { return; }
return response.text().then(function (body) {
return JSON.parse(body);
-h1 {\r
- text-align: center;\r
-}\r
-\r
-table.reportlist {\r
- margin: 2em auto 2em auto;\r
- border-collapse: collapse;\r
- clear: both;\r
-}\r
-\r
-table.reportlist td, table.reportlist th {\r
- padding:3px;\r
-}\r
-\r
-table.reportlist thead {\r
- border-top: 1px solid grey;\r
- border-bottom: 1px solid grey;\r
-\r
-}\r
-table.reportlist tbody tr:first-child td {\r
- padding-top: 10px;\r
-}\r
-table.reportlist tr.sum {\r
- border-top: 1px solid grey;\r
-}\r
-table.reportlist tr.selected {\r
- background-color: lightgrey;\r
-}\r
-.reportdesc {\r
- font-weight: bold;\r
- width: 90%;\r
- margin-left: auto;\r
- margin-right: auto;\r
-}\r
-\r
-tr.summaryrow {\r
- cursor: pointer;\r
-}\r
-\r
-tr.summaryrow:hover, tr.summaryrow.selected {\r
- background-color: lightgray;\r
- border-left: 1px solid lightgray;\r
-}\r
-\r
-td.reportcell {\r
- border-bottom: 1px solid lightgray;\r
- border-left: 1px solid lightgray;\r
- border-right: 1px solid lightgray;\r
-}\r
-\r
-table.reportdata {\r
- margin: 0px auto 0px auto;\r
- border-collapse: separate;\r
- border-spacing: 2px;\r
-}\r
-\r
-table.reportdata tr th, table.reportdata tr td {\r
- text-align: center;\r
- padding: 3px;\r
-}\r
-\r
-table.reportdata tr.red {\r
- background-color: #FF0000;\r
-}\r
-\r
-table.reportdata tr.orange {\r
- background-color: #FFA500;\r
-}\r
-\r
-table.reportdata tr.lime {\r
- background-color: #00FF00;\r
-}\r
-\r
-table.reportdata tr.yellow {\r
- background-color: #FFFF00;\r
-}\r
-\r
-.optionblock {\r
- background: lightgrey;\r
- padding: 0.4em;\r
- float: right;\r
- margin: auto 2em 1em auto;\r
- white-space: nowrap;\r
-}\r
-\r
-.optionlabel {\r
- font-weight: bold;\r
- float: left; clear: left; \r
- margin-right: 1em;\r
-}\r
-\r
-.options {\r
- font-size: 70%;\r
- text-align: right;\r
- border: none;\r
- width: 97%;\r
- padding: 0.4em;\r
-}\r
-\r
-.center {\r
- text-align:center;\r
-}\r
-\r
-.circle_lime:before {\r
- content: ' \25CF';\r
- font-size: 25px;\r
- color: #00FF00;\r
-}\r
-\r
-.circle_red:before {\r
- content: ' \25CF';\r
- font-size: 25px;\r
- color: #FF0000;\r
-}\r
-\r
-.circle_yellow:before {\r
- content: ' \25CF';\r
- font-size: 25px;\r
- color: #FFFF00;\r
-}\r
-\r
-.circle_orange:before {\r
- content: ' \25CF';\r
- font-size: 25px;\r
- color: #FFA500;\r
-}\r
+h1 {
+ text-align: center;
+}
+
+p.warninginfo {
+ text-align: center;
+}
+
+table.reportlist {
+ margin: 2em auto 2em auto;
+ border-collapse: collapse;
+ clear: both;
+}
+
+table.reportlist td, table.reportlist th {
+ padding:3px;
+}
+
+table.reportlist thead {
+ border-top: 1px solid grey;
+ border-bottom: 1px solid grey;
+
+}
+table.reportlist tbody tr:first-child td {
+ padding-top: 10px;
+}
+table.reportlist tr.sum {
+ border-top: 1px solid grey;
+}
+table.reportlist tr.selected {
+ background-color: lightgrey;
+}
+.reportdesc {
+ font-weight: bold;
+ width: 90%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+tr.summaryrow {
+ cursor: pointer;
+}
+
+tr.summaryrow:hover, tr.summaryrow.selected {
+ background-color: lightgray;
+ border-left: 1px solid lightgray;
+}
+
+td.reportcell {
+ border-bottom: 1px solid lightgray;
+ border-left: 1px solid lightgray;
+ border-right: 1px solid lightgray;
+}
+
+table.reportdata {
+ margin: 0px auto 0px auto;
+ border-collapse: separate;
+ border-spacing: 2px;
+}
+
+table.reportdata tr th, table.reportdata tr td {
+ text-align: center;
+ padding: 3px;
+}
+
+table.reportdata tr.red {
+ background-color: #FF0000;
+}
+
+table.reportdata tr.orange {
+ background-color: #FFA500;
+}
+
+table.reportdata tr.lime {
+ background-color: #00FF00;
+}
+
+table.reportdata tr.yellow {
+ background-color: #FFFF00;
+}
+
+.optionblock {
+ background: lightgrey;
+ padding: 0.4em;
+ float: right;
+ margin: auto 2em 1em auto;
+ white-space: nowrap;
+}
+
+.optionlabel {
+ font-weight: bold;
+ float: left; clear: left;
+ margin-right: 1em;
+}
+
+.options {
+ font-size: 70%;
+ text-align: right;
+ border: none;
+ width: 97%;
+ padding: 0.4em;
+}
+
+.center {
+ text-align:center;
+}
+
+.circle_lime:before {
+ content: ' \25CF';
+ font-size: 25px;
+ color: #00FF00;
+}
+
+.circle_red:before {
+ content: ' \25CF';
+ font-size: 25px;
+ color: #FF0000;
+}
+
+.circle_yellow:before {
+ content: ' \25CF';
+ font-size: 25px;
+ color: #FFFF00;
+}
+
+.circle_orange:before {
+ content: ' \25CF';
+ font-size: 25px;
+ color: #FFA500;
+}
<body>
<div id="app" style="width: 100%">
- <div class="optionblock" v-if="info">
+ <div class="optionblock">
+ <div class='options'>
+ <span class='optionlabel'>Anonymize</span>
+ <label><input type="radio" :value="false" v-model="anonymous" v-on:click="toggleAnonymous()"> no</label>
+ <label><input type="radio" :value="true" v-model="anonymous" v-on:click="toggleAnonymous()"> yes</label>
+ </div>
+ <template v-if="info">
<div class='options'>
<span class='optionlabel'>Hide all-green lines:</span>
<label><input type="radio" :value="false" v-model="filterGreen"> no</label>
<option v-for="date in info.dates" :value="date">{{ date }}</option>
</select>
</div>
+ </template>
</div>
<h1 class='main'>DMARC Reports</h1>
+ <p v-if="!info" class="warninginfo">
+ No information could be fetched. If in non-anonymous mode you need to be logged-in
+ </p>
<table class='reportlist' v-if="summaries">
<thead>
<tr>