apeters 1d204f26b8 pre-Korves.Net
Signed-off-by: apeters <apeters@korves.net>
2025-05-21 08:05:07 +00:00

212 lines
6.3 KiB
Python

import components.users
from components.utils import batch, ensure_list
from components.database import IN_MEMORY_DB
from components.models.users import UserGroups
from components.web.utils import *
blueprint = Blueprint("users", __name__, url_prefix="/system/users")
@blueprint.context_processor
def load_context():
from components.models.users import UserProfile
context = dict()
context["schemas"] = {"user_profile": UserProfile.model_json_schema()}
return context
@blueprint.route("/groups", methods=["PATCH"])
@acl("system")
async def user_group():
try:
request_data = UserGroups.parse_obj(request.form_parsed)
assigned_to = [
u
for u in await components.users.search(name="", join_credentials=False)
if request_data.name in u.groups
]
assign_to = []
for user_id in request_data.members:
assign_to.append(
await components.users.get(user_id=user_id, join_credentials=False)
)
_all = assigned_to + assign_to
async with ClusterLock(["users", "credentials"], current_app):
for user in _all:
user_dict = user.model_dump(mode="json")
if request_data.name in user_dict["groups"]:
user_dict["groups"].remove(request_data.name)
if (
request_data.new_name not in user_dict["groups"]
and user in assign_to
):
user_dict["groups"].append(request_data.new_name)
await components.users.patch(user_id=user.id, data=user_dict)
return "", 204
except ValidationError as e:
return validation_error(e.errors())
except ValueError as e:
name, message = e.args
return validation_error([{"loc": [name], "msg": message}])
@blueprint.route("/<user_id>")
@acl("system")
async def get_user(user_id: str):
try:
user = await components.users.get(user_id=user_id)
except ValidationError as e:
return validation_error(e.errors())
except ValueError as e:
name, message = e.args
return validation_error([{"loc": [name], "msg": message}])
return await render_or_json(
"system/includes/users/row.html", request.headers, user=user.dict()
)
@blueprint.route("/")
@blueprint.route("/search", methods=["POST"])
@acl("system")
@formoptions(["users"])
async def get_users():
try:
(
search_model,
page,
page_size,
sort_attr,
sort_reverse,
filters,
) = table_search_helper(request.form_parsed, "users", default_sort_attr="login")
except ValidationError as e:
return validation_error(e.errors())
if request.method == "POST":
matched_users = [
m.dict() for m in await components.users.search(name=search_model.q)
]
user_pages = [
m
for m in batch(
sorted(
matched_users,
key=lambda x: x.get(sort_attr, "id"),
reverse=sort_reverse,
),
page_size,
)
]
try:
user_pages[page - 1]
except IndexError:
page = len(user_pages)
users = user_pages[page - 1] if page else user_pages
return await render_template(
"system/includes/users/table_body.html",
data={
"users": users,
"page_size": page_size,
"page": page,
"pages": len(user_pages),
"elements": len(matched_users),
},
)
else:
return await render_template("system/users.html", data={})
@blueprint.route("/delete", methods=["POST"])
@blueprint.route("/<user_id>", methods=["DELETE"])
@acl("system")
async def delete_user(user_id: str | None = None):
if request.method == "POST":
user_ids = request.form_parsed.get("id")
try:
async with ClusterLock(["users", "credentials"], current_app):
for user_id in ensure_list(user_ids):
await components.users.delete(user_id=user_id)
except ValidationError as e:
return validation_error(e.errors())
except ValueError as e:
name, message = e.args
return validation_error([{"loc": [name], "msg": message}])
return trigger_notification(
level="success",
response_code=204,
title="User removed",
message=f"{len(ensure_list(user_ids))} user{'s' if len(ensure_list(user_ids)) > 1 else ''} removed",
)
@blueprint.route("/<user_id>/credential/<hex_id>", methods=["PATCH"])
@acl("system")
async def patch_user_credential(user_id: str, hex_id: str):
try:
async with ClusterLock("credentials", current_app):
await components.users.patch_credential(
user_id=user_id,
hex_id=hex_id,
data=request.form_parsed,
)
except ValidationError as e:
return validation_error(e.errors())
except ValueError as e:
name, message = e.args
return validation_error([{"loc": [name], "msg": message}])
return trigger_notification(
level="success",
response_code=204,
title="Credential modified",
message="Credential was modified",
)
@blueprint.route("/patch", methods=["POST"])
@blueprint.route("/<user_id>", methods=["PATCH"])
@acl("system")
async def patch_user(user_id: str | None = None):
try:
if not user_id:
user_id = request.form_parsed.get("id")
async with ClusterLock(["users", "credentials"], current_app):
await components.users.patch(user_id=user_id, data=request.form_parsed)
await components.users.patch_profile(
user_id=user_id, data=request.form_parsed.get("profile", {})
)
except ValidationError as e:
return validation_error(e.errors())
except ValueError as e:
name, message = e.args
return validation_error([{"loc": [name], "msg": message}])
IN_MEMORY_DB["SESSION_VALIDATED"].pop(user_id, None)
return trigger_notification(
level="success",
response_code=204,
title="User modified",
message=f"User was updated",
)