]> git.ipfire.org Git - ipfire.org.git/commitdiff
dnsbl: Add a simple page to view a report
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 30 Dec 2025 12:09:48 +0000 (12:09 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 30 Dec 2025 12:09:48 +0000 (12:09 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/backend/dnsbl.py
src/templates/dnsbl/report-submit.html [new file with mode: 0644]
src/templates/dnsbl/report.html
src/web/__init__.py
src/web/dnsbl.py

index a6f3ff7ba112d8d6083209aa5ddbcb3764cb5b5c..e2f8cef0bc5e146babcfc1231b9e980a4e6fa76c 100644 (file)
@@ -187,6 +187,7 @@ templates_dnsbl_DATA = \
        src/templates/dnsbl/index.html \
        src/templates/dnsbl/list.html \
        src/templates/dnsbl/report.html \
+       src/templates/dnsbl/report-submit.html \
        src/templates/dnsbl/report-submitted.html
 
 templates_dnsbldir = $(templatesdir)/dnsbl
index 8ce3274a7e088f6b3241c04d2fc0e60d7607bc28..fee08242e08eac0cc8830508f04c656e7f29453a 100644 (file)
@@ -6,6 +6,7 @@ import logging
 import pydantic
 import tornado.httpclient
 import urllib.parse
+import uuid
 
 from . import accounts
 from . import base
@@ -69,6 +70,25 @@ class DNSBL(Object):
                # Return the list
                return List(self.backend, **response)
 
+       # Reports
+
+       async def get_report(self, id):
+               """
+                       Fetches a report
+               """
+               try:
+                       response = await self._fetch("/reports/%s" % id)
+
+               # Return nothing if we received 404
+               except tornado.httpclient.HTTPClientError as e:
+                       if e.code == 404:
+                               return
+
+                       raise e
+
+               # Return the report
+               return Report(self.backend, **response)
+
 
 class Model(pydantic.BaseModel):
        """
@@ -210,8 +230,11 @@ class Report(Model):
        """
                Represents a report
        """
+       def __str__(self):
+               return self.name
+
        # ID
-       id : int
+       id : uuid.UUID
 
        # Name
        name : str
diff --git a/src/templates/dnsbl/report-submit.html b/src/templates/dnsbl/report-submit.html
new file mode 100644 (file)
index 0000000..93a623c
--- /dev/null
@@ -0,0 +1,162 @@
+{% extends "../base.html" %}
+
+{% block title %}
+       {{ _("IPFire DNSBL") }} {{ _("Report A Domain") }}
+{% end block %}
+
+{% block head %}
+       {% module OpenGraph(
+               title=_("Report A Domain"),
+               description=_("Help Us To Improve IPFire DNSBL"),
+       ) %}
+{% end block %}
+
+{% block container %}
+       <section class="hero is-primary">
+               <div class="hero-body">
+                       <div class="container">
+                               <h1 class="title">
+                                       {{ _("Help Us To Improve IPFire DNSBL") }}
+                               </h1>
+
+                               <p class="subtitle">
+                                       {{ _("Report anything that we have missed") }}
+                               </p>
+                       </div>
+               </div>
+       </section>
+
+       <section class="section">
+               <div class="container">
+                       {# Show a note to users that are not logged in #}
+                       {% if not current_user %}
+                               <div class="notification">
+                                       <strong>{{ _("Please log in to submit a report") }}</strong>
+
+                                       <br>
+
+                                       To keep the IPFire DNSBL accurate and trustworthy, domain reports
+                                       can only be submitted by logged-in users.
+                                       This helps us prevent spam and abuse, and ensures that every report
+                                       comes from a real, accountable community member.
+                               </div>
+                       {% end %}
+
+                       <form action="" method="POST">
+                               {% raw xsrf_form_html() %}
+
+                               <fieldset {% if not current_user %}disabled{% end %}>
+                                       {# List #}
+                                       <div class="field is-horizontal">
+                                               <div class="field-label is-normal">
+                                                       <label class="label">{{ _("List") }}</label>
+                                               </div>
+
+                                               <div class="field-body">
+                                                       <div class="field">
+                                                               <div class="control">
+                                                                       <div class="select is-fullwidth">
+                                                                               <select name="list" required>
+                                                                                       <option value="">- {{ _("Choose One") }} -</option>
+
+                                                                                       {% for l in sorted(lists) %}
+                                                                                               <option value="{{ l.slug }}">
+                                                                                                       {{ l.name }} {% if l.description %}&dash; {{ l.description }}{% end %}
+                                                                                               </option>
+                                                                                       {% end %}
+                                                                               </select>
+                                                                       </div>
+                                                               </div>
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       {# Name #}
+                                       <div class="field is-horizontal">
+                                               <div class="field-label is-normal">
+                                                       <label class="label">{{ _("Domain") }}</label>
+                                               </div>
+
+                                               <div class="field-body">
+                                                       <div class="field">
+                                                               <p class="control">
+                                                                       <input class="input" name="name" type="text" placeholder="{{ _("Domain") }}" required
+                                                                               pattern="^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$" />
+                                                               </p>
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       {# Block? #}
+                                       <div class="field is-horizontal">
+                                               <div class="field-label is-normal">
+                                                       <label class="label">{{ _("What is wrong?") }}</label>
+                                               </div>
+
+                                               <div class="field-body">
+                                                       <div class="field">
+                                                               <div class="control">
+                                                                       <div class="select is-fullwidth">
+                                                                               <select name="block" required>
+                                                                                       <option value="yes">
+                                                                                               {{ _("This domain should be blocked, but isn't") }}
+                                                                                       </option>
+
+                                                                                       <option value="no">
+                                                                                               {{ _("This domain should not be blocked, but currently is") }}
+                                                                                       </option>
+                                                                               </select>
+                                                                       </div>
+                                                               </div>
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       {# Comment #}
+                                       <div class="field is-horizontal">
+                                               <div class="field-label is-normal">
+                                                       <label class="label">{{ _("Additional Information") }}</label>
+                                               </div>
+
+                                               <div class="field-body">
+                                                       <div class="field">
+                                                               <p class="control">
+                                                                       <textarea class="textarea" name="comment" rows="4"></textarea>
+                                                               </p>
+
+                                                               <p class="help">
+                                                                       Please provide any additional context that may help us review this domain.
+                                                                       For example, where you encountered it or why you believe it should be (de-)listed.
+                                                               </p>
+
+                                                               <p class="help">
+                                                                       By submitting your report, you grant the IPFire Project the right to store,
+                                                                       process, and publish this information as part of its security services,
+                                                                       including the IPFire DNSBL under the terms of the respective list.
+                                                                       Submissions may be reviewed, modified, or removed at our discretion.
+                                                               </p>
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       {# Submit! #}
+                                       <div class="field is-horizontal">
+                                               <div class="field-label">
+                                                       <!-- Left empty for spacing -->
+                                               </div>
+
+                                               <div class="field-body">
+                                                       <div class="field">
+                                                               <div class="control">
+                                                                       <button type="submit" class="button is-primary">
+                                                                               {{ _("Submit Report") }}
+                                                                       </button>
+                                                               </div>
+                                                       </div>
+                                               </div>
+                                       </div>
+                               </fieldset>
+                       </form>
+               </div>
+       </section>
+{% end block %}
index 93a623c6bcf940cef56ac4b03bec04ef219fde58..d1913561d41ea71c0bb405ee7616f0a1b60c4fe5 100644 (file)
 {% extends "../base.html" %}
 
 {% block title %}
-       {{ _("IPFire DNSBL") }} {{ _("Report A Domain") }}
+       {{ _("IPFire DNSBL") }} {{ _("Report %s") % report }}
 {% end block %}
 
 {% block head %}
        {% module OpenGraph(
-               title=_("Report A Domain"),
-               description=_("Help Us To Improve IPFire DNSBL"),
+               title=_("Report %s") % report,
        ) %}
 {% end block %}
 
 {% block container %}
-       <section class="hero is-primary">
+       <section class="hero is-fullheight-with-navbar">
                <div class="hero-body">
-                       <div class="container">
-                               <h1 class="title">
-                                       {{ _("Help Us To Improve IPFire DNSBL") }}
-                               </h1>
-
-                               <p class="subtitle">
-                                       {{ _("Report anything that we have missed") }}
-                               </p>
-                       </div>
-               </div>
-       </section>
-
-       <section class="section">
-               <div class="container">
-                       {# Show a note to users that are not logged in #}
-                       {% if not current_user %}
-                               <div class="notification">
-                                       <strong>{{ _("Please log in to submit a report") }}</strong>
-
-                                       <br>
-
-                                       To keep the IPFire DNSBL accurate and trustworthy, domain reports
-                                       can only be submitted by logged-in users.
-                                       This helps us prevent spam and abuse, and ensures that every report
-                                       comes from a real, accountable community member.
-                               </div>
-                       {% end %}
-
-                       <form action="" method="POST">
-                               {% raw xsrf_form_html() %}
-
-                               <fieldset {% if not current_user %}disabled{% end %}>
-                                       {# List #}
-                                       <div class="field is-horizontal">
-                                               <div class="field-label is-normal">
-                                                       <label class="label">{{ _("List") }}</label>
-                                               </div>
-
-                                               <div class="field-body">
-                                                       <div class="field">
-                                                               <div class="control">
-                                                                       <div class="select is-fullwidth">
-                                                                               <select name="list" required>
-                                                                                       <option value="">- {{ _("Choose One") }} -</option>
-
-                                                                                       {% for l in sorted(lists) %}
-                                                                                               <option value="{{ l.slug }}">
-                                                                                                       {{ l.name }} {% if l.description %}&dash; {{ l.description }}{% end %}
-                                                                                               </option>
-                                                                                       {% end %}
-                                                                               </select>
-                                                                       </div>
-                                                               </div>
-                                                       </div>
-                                               </div>
-                                       </div>
-
-                                       {# Name #}
-                                       <div class="field is-horizontal">
-                                               <div class="field-label is-normal">
-                                                       <label class="label">{{ _("Domain") }}</label>
-                                               </div>
-
-                                               <div class="field-body">
-                                                       <div class="field">
-                                                               <p class="control">
-                                                                       <input class="input" name="name" type="text" placeholder="{{ _("Domain") }}" required
-                                                                               pattern="^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$" />
-                                                               </p>
-                                                       </div>
-                                               </div>
-                                       </div>
-
-                                       {# Block? #}
-                                       <div class="field is-horizontal">
-                                               <div class="field-label is-normal">
-                                                       <label class="label">{{ _("What is wrong?") }}</label>
-                                               </div>
-
-                                               <div class="field-body">
-                                                       <div class="field">
-                                                               <div class="control">
-                                                                       <div class="select is-fullwidth">
-                                                                               <select name="block" required>
-                                                                                       <option value="yes">
-                                                                                               {{ _("This domain should be blocked, but isn't") }}
-                                                                                       </option>
-
-                                                                                       <option value="no">
-                                                                                               {{ _("This domain should not be blocked, but currently is") }}
-                                                                                       </option>
-                                                                               </select>
-                                                                       </div>
-                                                               </div>
-                                                       </div>
+                       <div class="container is-flex is-justify-content-center is-align-items-center">
+                               <div class="card">
+                                       <div class="card-content">
+                                               <div class="content">
+                                                       <p class="title is-4">
+                                                               {{ report.name }}
+                                                       </p>
+
+                                                       <p class="subtitle is-6">
+                                                               {{ _("Submitted %(when)s by %(who)s") % {
+                                                                       "when" : locale.format_date(report.reported_at),
+                                                                       "who"  : report.reported_by,
+                                                               } }}
+                                                       </p>
+
+                                                       {# Comment #}
+                                                       {% if report.comment %}
+                                                               <pre>{{ report.comment }}</pre>
+                                                       {% end %}
                                                </div>
                                        </div>
-
-                                       {# Comment #}
-                                       <div class="field is-horizontal">
-                                               <div class="field-label is-normal">
-                                                       <label class="label">{{ _("Additional Information") }}</label>
-                                               </div>
-
-                                               <div class="field-body">
-                                                       <div class="field">
-                                                               <p class="control">
-                                                                       <textarea class="textarea" name="comment" rows="4"></textarea>
-                                                               </p>
-
-                                                               <p class="help">
-                                                                       Please provide any additional context that may help us review this domain.
-                                                                       For example, where you encountered it or why you believe it should be (de-)listed.
-                                                               </p>
-
-                                                               <p class="help">
-                                                                       By submitting your report, you grant the IPFire Project the right to store,
-                                                                       process, and publish this information as part of its security services,
-                                                                       including the IPFire DNSBL under the terms of the respective list.
-                                                                       Submissions may be reviewed, modified, or removed at our discretion.
-                                                               </p>
-                                                       </div>
-                                               </div>
-                                       </div>
-
-                                       {# Submit! #}
-                                       <div class="field is-horizontal">
-                                               <div class="field-label">
-                                                       <!-- Left empty for spacing -->
-                                               </div>
-
-                                               <div class="field-body">
-                                                       <div class="field">
-                                                               <div class="control">
-                                                                       <button type="submit" class="button is-primary">
-                                                                               {{ _("Submit Report") }}
-                                                                       </button>
-                                                               </div>
-                                                       </div>
-                                               </div>
-                                       </div>
-                               </fieldset>
-                       </form>
+                               </div>
+                       </div>
                </div>
        </section>
 {% end block %}
index 725c038b6c76a36c24ab44231fdc99e80cdeb396..67b25f08ad733f3004aff713a3de5014cecea844 100644 (file)
@@ -216,7 +216,8 @@ class Application(tornado.web.Application):
                        # DNSBL
                        (r"/dnsbl/?", dnsbl.IndexHandler),
                        (r"/dnsbl/lists/(\w+)", dnsbl.ListHandler),
-                       (r"/dnsbl/report", dnsbl.ReportHandler),
+                       (r"/dnsbl/report", dnsbl.SubmitReportHandler),
+                       (r"/dnsbl/reports/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", dnsbl.ReportHandler),
 
                        # Single-Sign-On for Discourse
                        (r"/sso/discourse", auth.SSODiscourse),
index 12dd9eac3da1a9a83fd52affd1ab238fc1425d32..85f66a4fb686540aa9adc870e93153f2da8ac792 100644 (file)
@@ -35,13 +35,13 @@ class ListHandler(base.AnalyticsMixin, BaseHandler):
                self.render("dnsbl/list.html", list=list, sources=sources)
 
 
-class ReportHandler(base.AnalyticsMixin, BaseHandler):
+class SubmitReportHandler(base.AnalyticsMixin, BaseHandler):
        async def get(self):
                # Fetch all lists
                lists = await self.backend.dnsbl.get_lists()
 
                # Render the page
-               self.render("dnsbl/report.html", lists=lists)
+               self.render("dnsbl/report-submit.html", lists=lists)
 
        @tornado.web.authenticated
        #@base.ratelimit(minutes=60, requests=10)
@@ -61,6 +61,16 @@ class ReportHandler(base.AnalyticsMixin, BaseHandler):
                self.render("dnsbl/report-submitted.html", report=report)
 
 
+class ReportHandler(base.AnalyticsMixin, BaseHandler):
+       async def get(self, id):
+               # Fetch the report
+               report = await self.backend.dnsbl.get_report(id)
+               if not report:
+                       raise tornado.web.HTTPError(404, "Could not find report '%s'" % id)
+
+               self.render("dnsbl/report.html", report=report)
+
+
 class ListsModule(ui_modules.UIModule):
        def render(self, lists):
                return self.render_string("dnsbl/modules/lists.html", lists=lists)