Restrict editor role navigation
This commit is contained in:
@@ -16,7 +16,7 @@ from zoneinfo import ZoneInfo
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from fastapi import FastAPI, File, Form, Request, UploadFile, status
|
from fastapi import FastAPI, File, Form, Request, UploadFile, status
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -449,6 +449,34 @@ def base_context(request: Request, user: dict | None, **extra: Any) -> dict[str,
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
EDITOR_ALLOWED_PATHS = (
|
||||||
|
"/",
|
||||||
|
"/raw",
|
||||||
|
"/editor",
|
||||||
|
"/sources",
|
||||||
|
"/uploads",
|
||||||
|
"/logout",
|
||||||
|
"/login",
|
||||||
|
"/favi.png",
|
||||||
|
"/favicon.ico",
|
||||||
|
"/health",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_editor_allowed_path(path: str) -> bool:
|
||||||
|
return any(path == allowed or path.startswith(f"{allowed}/") for allowed in EDITOR_ALLOWED_PATHS if allowed != "/") or path == "/"
|
||||||
|
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def restrict_editor_access(request: Request, call_next):
|
||||||
|
user = await get_current_user(request)
|
||||||
|
if user and user.get("role") == "editor" and not is_editor_allowed_path(request.url.path):
|
||||||
|
if request.method == "GET" and not request.headers.get("hx-request"):
|
||||||
|
return redirect("/raw")
|
||||||
|
return JSONResponse({"detail": "Forbidden"}, status_code=status.HTTP_403_FORBIDDEN)
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
|
|
||||||
def client_ip(request: Request) -> str:
|
def client_ip(request: Request) -> str:
|
||||||
forwarded = request.headers.get("x-forwarded-for", "")
|
forwarded = request.headers.get("x-forwarded-for", "")
|
||||||
if forwarded:
|
if forwarded:
|
||||||
|
|||||||
@@ -178,6 +178,7 @@
|
|||||||
<i data-lucide="database" class="w-5 h-5"></i>
|
<i data-lucide="database" class="w-5 h-5"></i>
|
||||||
Источники
|
Источники
|
||||||
</a>
|
</a>
|
||||||
|
{% if user.role != "editor" %}
|
||||||
<a href="/workers" class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors {{ 'bg-app-primary/10 text-app-primary' if 'workers' in request.url.path else 'text-app-textMuted hover:bg-app-surfaceHover hover:text-app-textMain' }}">
|
<a href="/workers" class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors {{ 'bg-app-primary/10 text-app-primary' if 'workers' in request.url.path else 'text-app-textMuted hover:bg-app-surfaceHover hover:text-app-textMain' }}">
|
||||||
<i data-lucide="cpu" class="w-5 h-5"></i>
|
<i data-lucide="cpu" class="w-5 h-5"></i>
|
||||||
Воркеры
|
Воркеры
|
||||||
@@ -190,6 +191,7 @@
|
|||||||
<i data-lucide="file-text" class="w-5 h-5"></i>
|
<i data-lucide="file-text" class="w-5 h-5"></i>
|
||||||
Логи
|
Логи
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="p-4 border-t border-app-border">
|
<div class="p-4 border-t border-app-border">
|
||||||
@@ -228,7 +230,9 @@
|
|||||||
<a href="/raw" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'raw' in request.url.path else 'text-app-textMuted' }}">Сырые посты</a>
|
<a href="/raw" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'raw' in request.url.path else 'text-app-textMuted' }}">Сырые посты</a>
|
||||||
<a href="/editor" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'editor' in request.url.path else 'text-app-textMuted' }}">Редактор</a>
|
<a href="/editor" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'editor' in request.url.path else 'text-app-textMuted' }}">Редактор</a>
|
||||||
<a href="/sources" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'sources' in request.url.path else 'text-app-textMuted' }}">Источники</a>
|
<a href="/sources" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'sources' in request.url.path else 'text-app-textMuted' }}">Источники</a>
|
||||||
|
{% if user.role != "editor" %}
|
||||||
<a href="/workers" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'workers' in request.url.path else 'text-app-textMuted' }}">Воркеры</a>
|
<a href="/workers" class="block px-3 py-2 rounded-lg text-sm {{ 'bg-app-bg text-app-textMain' if 'workers' in request.url.path else 'text-app-textMuted' }}">Воркеры</a>
|
||||||
|
{% endif %}
|
||||||
<div class="pt-2 mt-2 border-t border-app-border">
|
<div class="pt-2 mt-2 border-t border-app-border">
|
||||||
<form method="post" action="/logout" class="m-0">
|
<form method="post" action="/logout" class="m-0">
|
||||||
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
||||||
|
|||||||
Reference in New Issue
Block a user