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
import pydantic
import tornado.httpclient
import urllib.parse
+import uuid
from . import accounts
from . import base
# 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):
"""
"""
Represents a report
"""
+ def __str__(self):
+ return self.name
+
# ID
- id : int
+ id : uuid.UUID
# Name
name : str
--- /dev/null
+{% 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 %}‐ {{ 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 %}
{% 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 %}‐ {{ 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 %}
# 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),
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)
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)