acl do /login

This commit is contained in:
Mateusz Gruszczyński 2025-03-26 00:11:10 +01:00
parent 26a9d803a1
commit 60754ec23b
4 changed files with 53 additions and 2 deletions

1
alters.txt Normal file
View File

@ -0,0 +1 @@
ALTER TABLE global_settings ADD COLUMN allowed_login_hosts TEXT;

47
app.py
View File

@ -5,7 +5,10 @@ from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime from datetime import datetime
from markupsafe import Markup from markupsafe import Markup
import markdown as md import markdown as md
from flask import abort from flask import request, flash, abort
import os
import re
import socket
app = Flask(__name__) app = Flask(__name__)
# Ładujemy konfigurację z pliku config.py # Ładujemy konfigurację z pliku config.py
@ -52,11 +55,32 @@ class GlobalSettings(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
numer_konta = db.Column(db.String(50), nullable=False) numer_konta = db.Column(db.String(50), nullable=False)
numer_telefonu_blik = db.Column(db.String(50), nullable=False) numer_telefonu_blik = db.Column(db.String(50), nullable=False)
allowed_login_hosts = db.Column(db.Text, nullable=True)
@login_manager.user_loader @login_manager.user_loader
def load_user(user_id): def load_user(user_id):
return User.query.get(int(user_id)) return User.query.get(int(user_id))
def is_allowed_ip(remote_ip, allowed_hosts_str):
# Jeśli istnieje plik awaryjny, zawsze zezwalamy na dostęp
if os.path.exists("emergency_access.txt"):
return True
# Rozdzielamy wpisy mogą być oddzielone przecinkami lub znakami nowej linii
allowed_hosts = re.split(r'[\n,]+', allowed_hosts_str.strip())
allowed_ips = set()
for host in allowed_hosts:
host = host.strip()
if not host:
continue
try:
# Rozwiązywanie nazwy domeny do adresu IP.
resolved_ip = socket.gethostbyname(host)
allowed_ips.add(resolved_ip)
except Exception:
# Jeśli rozwiązywanie nazwy nie powiedzie się, pomijamy ten wpis.
continue
return remote_ip in allowed_ips
# Dodaj filtr Markdown pozwala na zagnieżdżanie linków i obrazków w opisie # Dodaj filtr Markdown pozwala na zagnieżdżanie linków i obrazków w opisie
@app.template_filter('markdown') @app.template_filter('markdown')
@ -86,6 +110,17 @@ def zbiorka(zbiorka_id):
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
# Pobierz ustawienia globalne, w tym dozwolone hosty
settings = GlobalSettings.query.first()
allowed_hosts_str = ""
if settings and settings.allowed_login_hosts:
allowed_hosts_str = settings.allowed_login_hosts
# Sprawdzenie, czy adres IP klienta jest dozwolony
if not is_allowed_ip(request.remote_addr, allowed_hosts_str):
flash('Dostęp do endpointu /login jest zablokowany dla Twojego adresu IP', 'danger')
return redirect(url_for('index'))
if request.method == 'POST': if request.method == 'POST':
username = request.form['username'] username = request.form['username']
password = request.form['password'] password = request.form['password']
@ -99,6 +134,7 @@ def login():
flash('Nieprawidłowe dane logowania', 'danger') flash('Nieprawidłowe dane logowania', 'danger')
return render_template('login.html') return render_template('login.html')
@app.route('/logout') @app.route('/logout')
@login_required @login_required
def logout(): def logout():
@ -290,13 +326,19 @@ def admin_settings():
if request.method == 'POST': if request.method == 'POST':
numer_konta = request.form.get('numer_konta') numer_konta = request.form.get('numer_konta')
numer_telefonu_blik = request.form.get('numer_telefonu_blik') numer_telefonu_blik = request.form.get('numer_telefonu_blik')
allowed_login_hosts = request.form.get('allowed_login_hosts')
if settings is None: if settings is None:
settings = GlobalSettings(numer_konta=numer_konta, numer_telefonu_blik=numer_telefonu_blik) settings = GlobalSettings(
numer_konta=numer_konta,
numer_telefonu_blik=numer_telefonu_blik,
allowed_login_hosts=allowed_login_hosts
)
db.session.add(settings) db.session.add(settings)
else: else:
settings.numer_konta = numer_konta settings.numer_konta = numer_konta
settings.numer_telefonu_blik = numer_telefonu_blik settings.numer_telefonu_blik = numer_telefonu_blik
settings.allowed_login_hosts = allowed_login_hosts
db.session.commit() db.session.commit()
flash('Ustawienia globalne zostały zaktualizowane', 'success') flash('Ustawienia globalne zostały zaktualizowane', 'success')
@ -304,6 +346,7 @@ def admin_settings():
return render_template('admin/settings.html', settings=settings) return render_template('admin/settings.html', settings=settings)
@app.route('/robots.txt') @app.route('/robots.txt')
def robots(): def robots():
if app.config.get("BLOCK_BOTS", False): if app.config.get("BLOCK_BOTS", False):

3
emergency_access Normal file
View File

@ -0,0 +1,3 @@
Jeśli ten plik istwnieje w katalogu apliakcji, to wylacza zebzpieczenie logowania do panelu admina z ograniczeniem IP.
Musi miec rozszerzenie .txt

View File

@ -16,6 +16,10 @@
<label for="numer_telefonu_blik" class="form-label">Globalny numer telefonu BLIK</label> <label for="numer_telefonu_blik" class="form-label">Globalny numer telefonu BLIK</label>
<input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik" value="{{ settings.numer_telefonu_blik if settings else '' }}" required> <input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik" value="{{ settings.numer_telefonu_blik if settings else '' }}" required>
</div> </div>
<div class="mb-3">
<label for="allowed_login_hosts" class="form-label">Dozwolone hosty logowania</label>
<textarea class="form-control" id="allowed_login_hosts" name="allowed_login_hosts" rows="4" placeholder="Podaj adresy IP lub nazwy domen oddzielone przecinkami lub nowymi liniami">{{ settings.allowed_login_hosts if settings and settings.allowed_login_hosts else '' }}</textarea>
</div>
<button type="submit" class="btn btn-primary">Zapisz ustawienia</button> <button type="submit" class="btn btn-primary">Zapisz ustawienia</button>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-secondary">Powrót</a> <a href="{{ url_for('admin_dashboard') }}" class="btn btn-secondary">Powrót</a>
</form> </form>