modified: web/app.py

modified:   web/templates/admin/base.html
	modified:   web/templates/auth/admin_login.html
	modified:   web/templates/auth/login.html
	modified:   web/templates/base.html
	modified:   web/templates/group_admin/base.html
	new file:   web/templates/privacy_policy.html
This commit is contained in:
simon
2026-04-14 12:33:51 +02:00
parent 8f614a08cc
commit 452d50e5b5
7 changed files with 240 additions and 0 deletions

View File

@@ -66,6 +66,15 @@ def create_app() -> Flask:
resp.headers.setdefault("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' https://cdn.jsdelivr.net; connect-src 'self'; frame-ancestors 'none';")
return resp
@app.route("/privacy-policy")
def privacy_policy():
from config import Config
return render_template(
"privacy_policy.html",
last_updated="April 14, 2026",
invite_expiry_hours=Config.INVITE_EXPIRY_HOURS,
)
@app.errorhandler(400)
def bad_request(_):
return "Bad request", 400

View File

@@ -40,6 +40,12 @@
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
<footer class="text-center py-3 mt-4 border-top border-secondary">
<small class="text-muted">
<a href="{{ url_for('privacy_policy') }}" class="text-muted text-decoration-none">Privacy Policy</a>
&mdash; MCLogger
</small>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}

View File

@@ -58,6 +58,9 @@
<i class="bi bi-arrow-left me-1"></i>Back to regular login
</a>
</div>
<div class="text-center mt-3">
<a href="{{ url_for('privacy_policy') }}" class="text-muted small">Privacy Policy</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>

View File

@@ -58,6 +58,9 @@
<i class="bi bi-shield-fill me-1"></i>Site Admin Login
</a>
</div>
<div class="text-center mt-3">
<a href="{{ url_for('privacy_policy') }}" class="text-muted small">Privacy Policy</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>

View File

@@ -171,6 +171,12 @@
{% endwith %}
<main class="px-4 py-3">{% block content %}{% endblock %}</main>
<footer class="text-center py-2 border-top border-secondary">
<small class="text-muted">
<a href="{{ url_for('privacy_policy') }}" class="text-muted text-decoration-none">Privacy Policy</a>
&mdash; MCLogger
</small>
</footer>
</div>
</div>

View File

@@ -42,6 +42,12 @@
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
<footer class="text-center py-3 mt-4 border-top border-secondary">
<small class="text-muted">
<a href="{{ url_for('privacy_policy') }}" class="text-muted text-decoration-none">Privacy Policy</a>
&mdash; MCLogger
</small>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>

View File

@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy — MCLogger</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
body { background: #0d1117; }
.policy-card { max-width: 820px; margin: 0 auto; }
h2 { font-size: 1.15rem; margin-top: 2rem; }
h3 { font-size: 1rem; margin-top: 1.25rem; }
p, li { color: #c9d1d9; }
</style>
</head>
<body class="py-5 px-3">
<div class="policy-card">
<div class="text-center mb-5">
<i class="bi bi-database-fill-gear fs-1 text-success"></i>
<h1 class="fw-bold mt-2 h3">MCLogger — Privacy Policy</h1>
<p class="text-muted small">Last updated: {{ last_updated }}</p>
</div>
<div class="card border-secondary mb-4">
<div class="card-body">
<h2 class="fw-semibold border-bottom border-secondary pb-2">1. Controller &amp; Contact</h2>
<p>
The controller responsible for data processing within this service is:
</p>
<p>
<strong>Simon</strong><br>
E-Mail: <a href="mailto:simon@devanturas.net">simon@devanturas.net</a>
</p>
<p>
For any questions, requests, or concerns regarding your personal data, please contact the
address above.
</p>
<h2 class="fw-semibold border-bottom border-secondary pb-2">2. What Is MCLogger?</h2>
<p>
MCLogger is a self-hosted logging and analytics panel for Minecraft server operators.
It collects and displays in-game activity data (sessions, chat, commands, deaths, block
events, proxy events) and provides a multi-tenant web interface for authorised server
administrators and group members.
</p>
<h2 class="fw-semibold border-bottom border-secondary pb-2">3. Data We Collect</h2>
<h3 class="fw-semibold">3.1 Minecraft Player Data</h3>
<p>When players connect to a Minecraft server that uses the MCLogger plugin, the
following data is automatically recorded:</p>
<ul>
<li><strong>Player identity:</strong> Minecraft username, UUID</li>
<li><strong>Sessions:</strong> join time, leave time, session duration, server name</li>
<li><strong>IP addresses:</strong> the IP address used at connection time</li>
<li><strong>Chat messages:</strong> message content, timestamp, username</li>
<li><strong>Commands:</strong> command text, timestamp, username</li>
<li><strong>Deaths:</strong> death message/cause, timestamp, location, username</li>
<li><strong>Block events:</strong> block type, action (place/break), coordinates, username, timestamp</li>
<li><strong>Proxy events:</strong> connect/disconnect events on the proxy network, timestamp</li>
</ul>
<p>
This data is stored in a MariaDB database operated by the server operator. Players should
be informed about this logging by the Minecraft server's own rules or MOTD.
</p>
<h3 class="fw-semibold">3.2 Panel User Accounts</h3>
<p>When a user account is created for the web panel, the following data is stored:</p>
<ul>
<li><strong>Username</strong></li>
<li><strong>Password</strong> (stored as a salted PBKDF2-HMAC-SHA256 hash; the plain-text password is never stored)</li>
<li><strong>E-mail address</strong> (when provided via the invite flow)</li>
<li><strong>Group membership and role</strong></li>
<li><strong>Account creation date</strong></li>
</ul>
<h3 class="fw-semibold">3.3 Invite Tokens</h3>
<p>
When a group administrator invites a user by e-mail, a time-limited invite token is
generated and stored together with the recipient's e-mail address. The token expires
after {{ invite_expiry_hours }} hours. Accepted and revoked tokens are retained in the database for
audit purposes.
</p>
<h3 class="fw-semibold">3.4 Session Data</h3>
<p>
MCLogger uses server-side sessions (Flask session cookie) to keep you logged in.
The session cookie is HTTP-only, SameSite-protected, and expires when your browser
session ends.
</p>
<h3 class="fw-semibold">3.5 Server Log Files</h3>
<p>
The web server (gunicorn) may write standard HTTP access logs containing IP
addresses, request paths, and timestamps. These logs are used for operational
security monitoring and are not shared with third parties.
</p>
<h2 class="fw-semibold border-bottom border-secondary pb-2">4. Purpose &amp; Legal Basis of Processing</h2>
<table class="table table-sm table-dark table-bordered">
<thead class="table-secondary">
<tr>
<th>Data</th>
<th>Purpose</th>
<th>Legal Basis (GDPR)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Minecraft player activity data</td>
<td>Server administration, moderation, abuse prevention</td>
<td>Art. 6(1)(f) — legitimate interest of the server operator</td>
</tr>
<tr>
<td>Panel user accounts</td>
<td>Authentication and authorisation for the web panel</td>
<td>Art. 6(1)(b) — performance of a contract / access service</td>
</tr>
<tr>
<td>E-mail addresses (invites)</td>
<td>Sending one-time panel invitation links</td>
<td>Art. 6(1)(a) — consent (the invite was requested by a group admin)</td>
</tr>
<tr>
<td>Server access logs</td>
<td>Security monitoring and error diagnosis</td>
<td>Art. 6(1)(f) — legitimate interest</td>
</tr>
</tbody>
</table>
<h2 class="fw-semibold border-bottom border-secondary pb-2">5. Data Retention</h2>
<ul>
<li><strong>Minecraft logs</strong> are retained as long as the server operator deems necessary for moderation purposes.</li>
<li><strong>Panel accounts</strong> are retained until manually deleted by a site administrator.</li>
<li><strong>Invite tokens</strong> expire after {{ invite_expiry_hours }} hours and are never sent to third parties beyond the intended recipient.</li>
<li><strong>Server access logs</strong> are typically rotated within 30 days.</li>
</ul>
<h2 class="fw-semibold border-bottom border-secondary pb-2">6. Data Sharing &amp; Third Parties</h2>
<p>
Data collected by MCLogger is <strong>not sold, rented, or shared</strong> with third
parties. All data remains within the infrastructure controlled by the server operator.
No third-party analytics services, advertising networks, or tracking pixels are used.
</p>
<p>
External resources loaded by the web interface (Bootstrap CSS/JS and Bootstrap Icons)
are served from the jsDelivr CDN (<code>cdn.jsdelivr.net</code>). jsDelivr may process
your IP address as part of delivering these static files. Please consult
<a href="https://www.jsdelivr.com/privacy-policy-jsdelivr-net" target="_blank" rel="noopener noreferrer">jsDelivr's privacy policy</a>
for details.
</p>
<h2 class="fw-semibold border-bottom border-secondary pb-2">7. Security</h2>
<p>
MCLogger applies the following technical safeguards:
</p>
<ul>
<li>Passwords are hashed with PBKDF2-HMAC-SHA256 (per-user salt + server pepper).</li>
<li>Stored database credentials and SMTP credentials are encrypted with Fernet symmetric encryption before being written to the database.</li>
<li>CSRF tokens are enforced on all state-changing requests.</li>
<li>Security response headers (X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, Referrer-Policy) are set on every response.</li>
<li>SMTP connections use STARTTLS.</li>
</ul>
<h2 class="fw-semibold border-bottom border-secondary pb-2">8. Your Rights (GDPR)</h2>
<p>If you are subject to the GDPR you have the following rights:</p>
<ul>
<li><strong>Right of access</strong> (Art. 15) — request a copy of your personal data.</li>
<li><strong>Right to rectification</strong> (Art. 16) — request correction of inaccurate data.</li>
<li><strong>Right to erasure</strong> (Art. 17) — request deletion of your data ("right to be forgotten").</li>
<li><strong>Right to restriction of processing</strong> (Art. 18) — request that processing is restricted while a dispute is resolved.</li>
<li><strong>Right to data portability</strong> (Art. 20) — receive your data in a structured, machine-readable format.</li>
<li><strong>Right to object</strong> (Art. 21) — object to processing based on legitimate interest.</li>
<li><strong>Right to withdraw consent</strong> (Art. 7(3)) — withdraw any consent you have given at any time.</li>
</ul>
<p>
To exercise any of these rights, please contact:
<a href="mailto:simon@devanturas.net">simon@devanturas.net</a>
</p>
<p>
You also have the right to lodge a complaint with your national data protection
supervisory authority.
</p>
<h2 class="fw-semibold border-bottom border-secondary pb-2">9. Changes to This Policy</h2>
<p>
This privacy policy may be updated to reflect changes in the software or applicable law.
The "Last updated" date at the top of this page indicates when the most recent revision
was made.
</p>
</div>
</div>
<div class="text-center text-muted small pb-5">
<a href="{{ url_for('auth.login') }}" class="text-muted me-3">
<i class="bi bi-arrow-left me-1"></i>Back to Login
</a>
MCLogger &mdash; <a href="mailto:simon@devanturas.net" class="text-muted">simon@devanturas.net</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>