add auth support
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from flask import Blueprint, render_template, jsonify, Response
|
||||
from flask import Blueprint, render_template, jsonify, Response, request, redirect, url_for, abort
|
||||
from ..services.preferences import get_preferences, list_profiles, active_profile, BOOTSTRAP_THEMES, FONT_FAMILIES, bootstrap_css_url
|
||||
from ..services import auth
|
||||
|
||||
bp = Blueprint("main", __name__)
|
||||
|
||||
|
||||
@bp.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
# Note: When optional authentication is disabled, /login is intentionally unavailable.
|
||||
if not auth.enabled():
|
||||
abort(404)
|
||||
error = ""
|
||||
if request.method == "POST":
|
||||
user = auth.login_user(request.form.get("username", ""), request.form.get("password", ""))
|
||||
if user:
|
||||
return redirect(request.args.get("next") or url_for("main.index"))
|
||||
error = "Invalid username or password"
|
||||
return render_template("login.html", error=error)
|
||||
|
||||
|
||||
@bp.get("/logout")
|
||||
def logout():
|
||||
auth.logout_user()
|
||||
if not auth.enabled():
|
||||
return redirect(url_for("main.index"))
|
||||
return redirect(url_for("main.login"))
|
||||
|
||||
|
||||
@bp.get("/")
|
||||
def index():
|
||||
prefs = get_preferences()
|
||||
@@ -17,6 +40,8 @@ def index():
|
||||
bootstrap_themes=BOOTSTRAP_THEMES,
|
||||
font_families=FONT_FAMILIES,
|
||||
bootstrap_css_url=bootstrap_css_url((prefs or {}).get("bootstrap_theme")),
|
||||
auth_enabled=auth.enabled(),
|
||||
current_user=auth.current_user(),
|
||||
)
|
||||
|
||||
|
||||
@@ -80,7 +105,13 @@ def openapi():
|
||||
"/api/traffic/history": {"get": {"summary": "Transfer history for charts", "parameters": [{"name": "range", "in": "query", "schema": {"type": "string", "enum": ["15m", "1h", "3h", "6h", "24h", "7d", "30d", "90d"]}}], "responses": {"200": {"description": "Aggregated traffic history"}}}}
|
||||
}
|
||||
paths.update({
|
||||
"/api/auth/login": {"post": {"summary": "Log in with username and password when authentication is enabled", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"username": {"type": "string"}, "password": {"type": "string", "format": "password"}}, "required": ["username", "password"]}}}}, "responses": {"200": {"description": "Logged in"}, "401": {"description": "Invalid credentials"}, "404": {"description": "Authentication disabled"}}}},
|
||||
"/api/auth/me": {"get": {"summary": "Return current authenticated user", "responses": {"200": {"description": "Current user"}, "404": {"description": "Authentication disabled"}}}},
|
||||
"/api/auth/logout": {"post": {"summary": "Log out current user", "responses": {"200": {"description": "Logged out"}, "404": {"description": "Authentication disabled"}}}},
|
||||
"/api/auth/users": {"get": {"summary": "List users, admin only", "responses": {"200": {"description": "Users"}, "403": {"description": "Admin only"}}}, "post": {"summary": "Create user, admin only", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AuthUserInput"}}}}, "responses": {"200": {"description": "User created"}, "403": {"description": "Admin only"}}}},
|
||||
"/api/auth/users/{user_id}": {"put": {"summary": "Update user, admin only", "parameters": [{"name": "user_id", "in": "path", "required": True, "schema": {"type": "integer"}}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AuthUserInput"}}}}, "responses": {"200": {"description": "User updated"}, "403": {"description": "Admin only"}}}, "delete": {"summary": "Delete user, admin only", "parameters": [{"name": "user_id", "in": "path", "required": True, "schema": {"type": "integer"}}], "responses": {"200": {"description": "User deleted"}, "403": {"description": "Admin only"}}}},
|
||||
"/api/profiles/{profile_id}": {"delete": {"summary": "Delete rTorrent profile", "parameters": [{"name": "profile_id", "in": "path", "required": True, "schema": {"type": "integer"}}], "responses": {"200": {"description": "Deleted"}}}},
|
||||
"/api/torrent-stats": {"get": {"summary": "Torrent statistics and cached file metadata", "parameters": [{"name": "force", "in": "query", "schema": {"type": "boolean", "default": False}}], "responses": {"200": {"description": "Torrent statistics"}}}},
|
||||
"/api/path/default": {"get": {"summary": "Read active rTorrent default download path", "responses": {"200": {"description": "Default path"}}}},
|
||||
"/api/torrents/{torrent_hash}/files/priority": {"post": {"summary": "Set file priorities", "parameters": [{"name": "torrent_hash", "in": "path", "required": True, "schema": {"type": "string"}}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"files": {"type": "array", "items": {"type": "object", "properties": {"index": {"type": "integer"}, "priority": {"type": "integer", "enum": [0, 1, 2]}}}}}}}}}, "responses": {"200": {"description": "Updated priorities"}, "207": {"description": "Partial update"}}}},
|
||||
"/api/torrents/{torrent_hash}/peers/action": {"post": {"summary": "Run peer action", "parameters": [{"name": "torrent_hash", "in": "path", "required": True, "schema": {"type": "string"}}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"peer_index": {"type": "integer"}, "action": {"type": "string", "enum": ["disconnect", "kick", "snub", "unsnub", "ban"]}}}}}}, "responses": {"200": {"description": "Peer action result"}}}},
|
||||
@@ -98,6 +129,17 @@ def openapi():
|
||||
"properties": {"ok": {"type": "boolean"}},
|
||||
"required": ["ok"],
|
||||
},
|
||||
"AuthUserInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {"type": "string"},
|
||||
"password": {"type": "string", "format": "password", "description": "Optional on update"},
|
||||
"role": {"type": "string", "enum": ["admin", "user"]},
|
||||
"is_active": {"type": "boolean"},
|
||||
"permissions": {"type": "array", "items": {"type": "object", "properties": {"profile_id": {"type": "integer", "description": "0 means all profiles"}, "access_level": {"type": "string", "enum": ["ro", "full"]}}}},
|
||||
},
|
||||
"required": ["username"],
|
||||
},
|
||||
"Profile": {
|
||||
"type": "object",
|
||||
"additionalProperties": True,
|
||||
@@ -278,4 +320,9 @@ def openapi():
|
||||
},
|
||||
})
|
||||
|
||||
components.setdefault("securitySchemes", {})["sessionCookie"] = {"type": "apiKey", "in": "cookie", "name": "session"}
|
||||
for path, methods in paths.items():
|
||||
if path != "/api/auth/login":
|
||||
for operation in methods.values():
|
||||
operation.setdefault("security", [{"sessionCookie": []}])
|
||||
return jsonify({"openapi": "3.0.3", "info": {"title": "pyTorrent API", "version": "0.2.0"}, "paths": paths, "components": components})
|
||||
|
||||
Reference in New Issue
Block a user