sprava uzivatelu
This commit is contained in:
+9
-1
@@ -9,7 +9,7 @@ from urllib.request import urlopen
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
|
||||
from app.auth import authenticate_user, current_user, find_or_create_oauth_user
|
||||
from app.auth import authenticate_user, current_user, find_or_create_oauth_user, mark_last_login
|
||||
from app.config import (
|
||||
get_gitea_public_url,
|
||||
get_gitea_redirect_uri,
|
||||
@@ -114,12 +114,16 @@ def gitea_callback(request: Request, code: str = "", state: str = "", error: str
|
||||
display_name = (gitea_user.get("full_name") or username).strip()
|
||||
email = (gitea_user.get("email") or "").strip()
|
||||
user = find_or_create_oauth_user(username, display_name, email)
|
||||
if not user.get("is_enabled", 1):
|
||||
_log_gitea_failure("disabled_user", username=username)
|
||||
return _render_login("User is disabled in the portal.")
|
||||
if not user.get("is_active"):
|
||||
_log_gitea_failure("disabled_user", username=username)
|
||||
return _render_login("Uživatel je v portálu vypnutý.")
|
||||
|
||||
request.session.clear()
|
||||
request.session["user_id"] = user["id"]
|
||||
mark_last_login(int(user["id"]))
|
||||
log_audit_event(
|
||||
user,
|
||||
action="auth.gitea.login.success",
|
||||
@@ -201,12 +205,16 @@ def google_callback(request: Request, code: str = "", state: str = "", error: st
|
||||
username = email
|
||||
display_name = (google_user.get("name") or email).strip()
|
||||
user = find_or_create_oauth_user(username, display_name, email)
|
||||
if not user.get("is_enabled", 1):
|
||||
_log_google_failure("disabled_user", username=username)
|
||||
return _render_login("User is disabled in the portal.")
|
||||
if not user.get("is_active"):
|
||||
_log_google_failure("disabled_user", username=username)
|
||||
return _render_login("Uživatel je v portálu vypnutý.")
|
||||
|
||||
request.session.clear()
|
||||
request.session["user_id"] = user["id"]
|
||||
mark_last_login(int(user["id"]))
|
||||
log_audit_event(
|
||||
user,
|
||||
action="auth.google.login.success",
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import html
|
||||
from urllib.parse import quote
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
|
||||
from app.auth import require_user
|
||||
from app.db.audit import log_audit_event
|
||||
from app.db.users import ROLES, count_enabled_admins, get_user, list_users, set_user_enabled, update_user_role
|
||||
from app.templates.layout import page
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def require_admin(user: dict) -> None:
|
||||
if (user.get("role") or "").lower() != "admin":
|
||||
raise HTTPException(status_code=403, detail="Only admins can manage users")
|
||||
|
||||
|
||||
def render_role_options(selected: str) -> str:
|
||||
options = []
|
||||
for role in ROLES:
|
||||
selected_attr = " selected" if selected == role else ""
|
||||
options.append(f'<option value="{role}"{selected_attr}>{role}</option>')
|
||||
return "".join(options)
|
||||
|
||||
|
||||
def render_enabled_pill(enabled) -> str:
|
||||
if enabled:
|
||||
return '<span class="pill pill-success">enabled</span>'
|
||||
return '<span class="pill pill-muted">disabled</span>'
|
||||
|
||||
|
||||
@router.get("/admin/users", response_class=HTMLResponse)
|
||||
def users_page(request: Request, message: str = "", error: str = "", user=Depends(require_user)):
|
||||
require_admin(user)
|
||||
|
||||
notice = ""
|
||||
if message:
|
||||
notice = f'<p class="alert">{html.escape(message)}</p>'
|
||||
if error:
|
||||
notice = f'<p class="alert alert-danger">{html.escape(error)}</p>'
|
||||
|
||||
rows = ""
|
||||
for managed_user in list_users():
|
||||
user_id = int(managed_user.get("id"))
|
||||
user_id_html = html.escape(str(user_id))
|
||||
username = html.escape(managed_user.get("username", "") or "")
|
||||
display_name = html.escape(managed_user.get("display_name", "") or "")
|
||||
email = html.escape(managed_user.get("email", "") or "")
|
||||
role = html.escape(managed_user.get("role", "") or "")
|
||||
created_at = html.escape(managed_user.get("created_at", "") or "")
|
||||
last_login_at = html.escape(managed_user.get("last_login_at", "") or "")
|
||||
enabled = bool(managed_user.get("is_enabled", True))
|
||||
toggle_label = "Disable" if enabled else "Enable"
|
||||
toggle_action = "disable" if enabled else "enable"
|
||||
|
||||
rows += f"""
|
||||
<tr>
|
||||
<td>{user_id_html}</td>
|
||||
<td><strong>{username}</strong></td>
|
||||
<td>{display_name}</td>
|
||||
<td>{email}</td>
|
||||
<td>
|
||||
<form method="post" action="/portal/admin/users/{user_id_html}/role" class="inline-form">
|
||||
<select name="role">{render_role_options(role)}</select>
|
||||
<button type="submit" class="btn-secondary">Save</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>{render_enabled_pill(enabled)}</td>
|
||||
<td>{created_at}</td>
|
||||
<td>{last_login_at}</td>
|
||||
<td class="actions-cell">
|
||||
<form method="post" action="/portal/admin/users/{user_id_html}/{toggle_action}" class="inline-form">
|
||||
<button type="submit" class="btn-secondary">{toggle_label}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
if not rows:
|
||||
rows = '<tr><td colspan="9">No users found.</td></tr>'
|
||||
|
||||
return page(
|
||||
"Users",
|
||||
f"""
|
||||
<div class="card">
|
||||
<h2>Users</h2>
|
||||
<p class="muted">Administration of portal users and roles.</p>
|
||||
{notice}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>User List</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Display name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Status</th>
|
||||
<th>Created</th>
|
||||
<th>Last login</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
{rows}
|
||||
</table>
|
||||
</div>
|
||||
""",
|
||||
user=user,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/admin/users/{user_id}/role")
|
||||
def update_role_action(user_id: int, role: str = Form(...), user=Depends(require_user)):
|
||||
require_admin(user)
|
||||
target = get_user(user_id)
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if role not in ROLES:
|
||||
raise HTTPException(status_code=400, detail="Invalid role")
|
||||
|
||||
old_role = (target.get("role") or "").lower()
|
||||
if old_role == "admin" and role != "admin" and count_enabled_admins(excluding_user_id=user_id) == 0:
|
||||
return RedirectResponse(
|
||||
url="/portal/admin/users?error=" + quote("Cannot remove admin role from the last enabled admin."),
|
||||
status_code=303,
|
||||
)
|
||||
|
||||
update_user_role(user_id, role)
|
||||
if old_role != role:
|
||||
log_audit_event(
|
||||
user,
|
||||
action="user.role.updated",
|
||||
target_type="user",
|
||||
target_id=user_id,
|
||||
metadata={
|
||||
"username": target.get("username"),
|
||||
"old_role": old_role,
|
||||
"new_role": role,
|
||||
},
|
||||
)
|
||||
|
||||
return RedirectResponse(url="/portal/admin/users?message=" + quote("User role updated."), status_code=303)
|
||||
|
||||
|
||||
@router.post("/admin/users/{user_id}/disable")
|
||||
def disable_user_action(user_id: int, user=Depends(require_user)):
|
||||
require_admin(user)
|
||||
target = get_user(user_id)
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if (target.get("role") or "").lower() == "admin" and count_enabled_admins(excluding_user_id=user_id) == 0:
|
||||
return RedirectResponse(
|
||||
url="/portal/admin/users?error=" + quote("Cannot disable the last enabled admin."),
|
||||
status_code=303,
|
||||
)
|
||||
|
||||
set_user_enabled(user_id, False)
|
||||
log_audit_event(
|
||||
user,
|
||||
action="user.disabled",
|
||||
target_type="user",
|
||||
target_id=user_id,
|
||||
metadata={"username": target.get("username")},
|
||||
)
|
||||
return RedirectResponse(url="/portal/admin/users?message=" + quote("User disabled."), status_code=303)
|
||||
|
||||
|
||||
@router.post("/admin/users/{user_id}/enable")
|
||||
def enable_user_action(user_id: int, user=Depends(require_user)):
|
||||
require_admin(user)
|
||||
target = get_user(user_id)
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
set_user_enabled(user_id, True)
|
||||
log_audit_event(
|
||||
user,
|
||||
action="user.enabled",
|
||||
target_type="user",
|
||||
target_id=user_id,
|
||||
metadata={"username": target.get("username")},
|
||||
)
|
||||
return RedirectResponse(url="/portal/admin/users?message=" + quote("User enabled."), status_code=303)
|
||||
Reference in New Issue
Block a user