added csrf protection & fixed minor functionality on admin page
This commit is contained in:
parent
c5cd02a5ec
commit
f188af4abf
36
app.py
36
app.py
@ -1,10 +1,12 @@
|
||||
from flask import Flask, render_template, redirect, url_for, request
|
||||
from flask import Flask, render_template, redirect, url_for, request, abort
|
||||
from flask_wtf.csrf import CSRFProtect
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user
|
||||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
csrf = CSRFProtect(app)
|
||||
|
||||
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
|
||||
from urllib.parse import quote_plus
|
||||
@ -38,6 +40,7 @@ class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(100), unique=True)
|
||||
password = db.Column(db.String(200)) # Increased for password hash
|
||||
admin = db.Column(db.Boolean, default=False)
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
@ -81,6 +84,11 @@ def register():
|
||||
password = generate_password_hash(password)
|
||||
|
||||
new_user = User(email=email, password=password)
|
||||
|
||||
# Automatically make first user admin
|
||||
if User.query.count() == 0:
|
||||
new_user.admin = True
|
||||
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
|
||||
@ -99,3 +107,27 @@ def dashboard():
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for("login"))
|
||||
|
||||
@app.route("/admin", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def admin():
|
||||
if not current_user.admin:
|
||||
abort(403)
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.form.get("action")
|
||||
user_id = request.form.get("user_id")
|
||||
user = User.query.get(user_id)
|
||||
|
||||
if user and user != current_user: # Prevent self-modification
|
||||
if action == "promote":
|
||||
user.admin = True
|
||||
elif action == "demote":
|
||||
user.admin = False
|
||||
elif action == "delete":
|
||||
db.session.delete(user)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
users = User.query.all()
|
||||
return render_template("admin.html", users=users)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
flask==3.0.0
|
||||
flask-sqlalchemy==3.1.1
|
||||
flask-login==0.6.3
|
||||
flask-wtf==1.2.1
|
||||
werkzeug==3.0.0
|
||||
gunicorn==21.2.0
|
||||
psycopg2-binary==2.9.9
|
||||
|
||||
60
templates/admin.html
Normal file
60
templates/admin.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Admin Panel{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">Admin Dashboard</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>User Management</h5>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Email</th>
|
||||
<th>Admin Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user.id }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>{% if user.admin %}Admin{% else %}User{% endif %}</td>
|
||||
<td>
|
||||
{% if user.admin %}
|
||||
<form method="POST" action="{{ url_for('admin') }}" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="user_id" value="{{ user.id }}">
|
||||
<input type="hidden" name="action" value="demote">
|
||||
<button type="submit" class="btn btn-sm btn-warning">Remove Admin</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form method="POST" action="{{ url_for('admin') }}" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="user_id" value="{{ user.id }}">
|
||||
<input type="hidden" name="action" value="promote">
|
||||
<button type="submit" class="btn btn-sm btn-success">Make Admin</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<form method="POST" action="{{ url_for('admin') }}" class="d-inline" onsubmit="return confirm('Delete this user permanently?')">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="user_id" value="{{ user.id }}">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -30,6 +30,9 @@
|
||||
<div class="navbar-nav ms-auto">
|
||||
<a class="nav-link active" href="{{ url_for('dashboard') }}">Home</a>
|
||||
<a class="nav-link" href="#">Example1</a>
|
||||
{% if current_user.admin %}
|
||||
<a class="nav-link" href="{{ url_for('admin') }}">Admin</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-center mb-4">Sign In</h2>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" name="email" required>
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-center mb-4">Create Account</h2>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" name="email" required>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user