04/23 Enhance app functionalities
This commit is contained in:
@@ -39,6 +39,9 @@ class AdminUsersView(ttk.Frame):
|
||||
ttk.Button(toolbar, text="✕ Delete",
|
||||
style="Danger.TButton",
|
||||
command=self._delete_selected).pack(side="right")
|
||||
ttk.Button(toolbar, text="🔑 Reset Password",
|
||||
style="Ghost.TButton",
|
||||
command=self._reset_password).pack(side="right", padx=(0, 8))
|
||||
|
||||
# Treeview
|
||||
cols = ("ID", "Username", "Full Name", "Role", "Active", "Created")
|
||||
@@ -76,6 +79,82 @@ class AdminUsersView(ttk.Frame):
|
||||
|
||||
# ─── Dialogs ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _reset_password(self):
|
||||
"""
|
||||
Generate a random temporary password for the selected user, set it in
|
||||
the database, copy it to the clipboard, and log the action.
|
||||
The admin must communicate the temporary password to the user out-of-band;
|
||||
the user should change it immediately via Change Password.
|
||||
"""
|
||||
import random
|
||||
import string
|
||||
|
||||
uid = self._get_selected_id()
|
||||
if not uid:
|
||||
show_error("Please select a user to reset.")
|
||||
return
|
||||
if uid == self.current_user["id"]:
|
||||
show_error("You cannot reset your own password here.\nUse Change Password instead.")
|
||||
return
|
||||
|
||||
vals = self.tree.item(uid, "values")
|
||||
username = vals[1] if vals else str(uid)
|
||||
|
||||
# Confirm before proceeding
|
||||
from tkinter import messagebox
|
||||
if not messagebox.askyesno(
|
||||
"Reset Password",
|
||||
f"Generate a new temporary password for '{username}'?\n\n"
|
||||
"The temporary password will be copied to your clipboard.",
|
||||
icon="warning",
|
||||
):
|
||||
return
|
||||
|
||||
# Build a strong random password that meets the existing policy:
|
||||
# 8+ chars, uppercase, digit, special character
|
||||
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
while True:
|
||||
pwd = "".join(random.SystemRandom().choices(alphabet, k=16))
|
||||
if (any(c.isupper() for c in pwd)
|
||||
and any(c.isdigit() for c in pwd)
|
||||
and any(c in "!@#$%^&*" for c in pwd)):
|
||||
break
|
||||
|
||||
try:
|
||||
from models import update_user, get_user_by_id, log_action
|
||||
user_data = get_user_by_id(uid)
|
||||
if not user_data:
|
||||
show_error("User not found.")
|
||||
return
|
||||
update_user(
|
||||
self.current_user["id"],
|
||||
uid,
|
||||
user_data["username"],
|
||||
user_data["role"],
|
||||
user_data["full_name"],
|
||||
user_data["is_active"],
|
||||
password=pwd,
|
||||
)
|
||||
log_action(
|
||||
self.current_user["id"], "RESET_PASSWORD", "users", uid,
|
||||
f"Admin reset password for user '{username}' (id={uid})."
|
||||
)
|
||||
logger.info(
|
||||
f"Password reset for user id={uid} '{username}' "
|
||||
f"by admin '{self.current_user['username']}'."
|
||||
)
|
||||
# Copy to clipboard
|
||||
self.clipboard_clear()
|
||||
self.clipboard_append(pwd)
|
||||
show_info(
|
||||
f"Temporary password for '{username}' has been set and "
|
||||
f"copied to your clipboard:\n\n{pwd}\n\n"
|
||||
"Please share it with the user securely.\n"
|
||||
"The user should change it immediately after logging in."
|
||||
)
|
||||
except Exception as e:
|
||||
show_error(f"Password reset failed:\n{e}")
|
||||
|
||||
def _open_add_dialog(self):
|
||||
UserDialog(self, self.current_user, user_data=None,
|
||||
on_save=self._load_users)
|
||||
|
||||
Reference in New Issue
Block a user