""" Module for managing user accounts and authentication. Provides methods for creating, validating, and retrieving user information. """ ''' Copyright 2025-2026 AIIrondev Licensed under the Inventarsystem EULA (Endbenutzer-Lizenzvertrag). See Legal/LICENSE for the full license text. Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited. For commercial licensing inquiries: https://github.com/AIIrondev ''' import hashlib from bson.objectid import ObjectId import settings as cfg from settings import MongoClient def normalize_student_card_id(card_id): """Normalize student card IDs for reliable lookup.""" if card_id is None: return '' return str(card_id).strip().upper() # === FAVORITES MANAGEMENT === def get_favorites(username): """Return a list of favorite item ObjectId strings for the user.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'Username': username}) or users.find_one({'username': username}) client.close() if not user: return [] favs = user.get('favorites', []) # Normalize to strings return [str(f) for f in favs if f] def add_favorite(username, item_id): """Add an item to user's favorites (idempotent).""" try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users.update_one( {'$or': [{'Username': username}, {'username': username}]}, {'$addToSet': {'favorites': ObjectId(item_id)}} ) client.close() return True except Exception: return False def remove_favorite(username, item_id): """Remove an item from user's favorites.""" try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users.update_one( {'$or': [{'Username': username}, {'username': username}]}, {'$pull': {'favorites': ObjectId(item_id)}} ) client.close() return True except Exception: return False def check_password_strength(password): """ Check if a password meets minimum security requirements. Args: password (str): Password to check Returns: bool: True if password is strong enough, False otherwise """ if password is None: return False if len(password) < 12: return False has_lower = any(char.islower() for char in password) has_upper = any(char.isupper() for char in password) has_digit = any(char.isdigit() for char in password) has_symbol = any(not char.isalnum() for char in password) if not (has_lower and has_upper and has_digit and has_symbol): return False return True def hashing(password): """ Hash a password using SHA-512. Args: password (str): Password to hash Returns: str: Hexadecimal digest of the hashed password """ return hashlib.sha512(password.encode()).hexdigest() def check_nm_pwd(username, password): """ Verify username and password combination. Args: username (str): Username to check password (str): Password to verify Returns: dict: User document if credentials are valid, None otherwise """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] hashed_password = hashlib.sha512(password.encode()).hexdigest() user = users.find_one({'Username': username, 'Password': hashed_password}) client.close() return user def add_user(username, password, name, last_name, is_student=False, student_card_id=None, max_borrow_days=None): """ Add a new user to the database. Args: username (str): Username for the new user password (str): Password for the new user Returns: bool: True if user was added successfully, False if password was too weak """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] if not check_password_strength(password): return False user_doc = { 'Username': username, 'Password': hashing(password), 'Admin': False, 'active_ausleihung': None, 'name': name, 'last_name': last_name, 'IsStudent': bool(is_student) } normalized_card = normalize_student_card_id(student_card_id) if bool(is_student): if normalized_card: user_doc['StudentCardId'] = normalized_card if max_borrow_days is not None: try: user_doc['MaxBorrowDays'] = int(max_borrow_days) except (TypeError, ValueError): pass users.insert_one(user_doc) client.close() return True def student_card_exists(student_card_id): """Return True if a student card id is already assigned to a user.""" normalized = normalize_student_card_id(student_card_id) if not normalized: return False client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] exists = users.find_one({'StudentCardId': normalized}) is not None client.close() return exists def get_user_by_student_card(student_card_id): """Return user by student card id or None.""" normalized = normalize_student_card_id(student_card_id) if not normalized: return None client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] found_user = users.find_one({'StudentCardId': normalized}) client.close() return found_user def make_admin(username): """ Grant administrator privileges to a user. Args: username (str): Username of the user to promote Returns: bool: True if user was promoted successfully """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users.update_one({'Username': username}, {'$set': {'Admin': True}}) client.close() return True def remove_admin(username): """ Remove administrator privileges from a user. Args: username (str): Username of the user to demote Returns: bool: True if user was demoted successfully """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users.update_one({'Username': username}, {'$set': {'Admin': False}}) client.close() return True def get_user(username): """ Retrieve a specific user by username. Args: username (str): Username to search for Returns: dict: User document or None if not found """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users_return = users.find_one({'Username': username}) client.close() return users_return def check_admin(username): """ Check if a user has administrator privileges. Args: username (str): Username to check Returns: bool: True if user is an administrator, False otherwise """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'Username': username}) client.close() return user and user.get('Admin', False) def update_active_ausleihung(username, id_item, ausleihung): """ Update a user's active borrowing record. Args: username (str): Username of the user id_item (str): ID of the borrowed item ausleihung (str): ID of the borrowing record Returns: bool: True if successful """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] users.update_one({'Username': username}, {'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}}) client.close() return True def get_active_ausleihung(username): """ Get a user's active borrowing record. Args: username (str): Username of the user Returns: dict: Active borrowing information or None """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'Username': username}) return user['active_ausleihung'] def has_active_borrowing(username): """ Check if a user currently has an active borrowing. Args: username (str): Username to check Returns: bool: True if user has an active borrowing, False otherwise """ try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'username': username}) if not user: user = users.find_one({'Username': username}) if not user: client.close() return False has_active = user.get('active_borrowing', False) client.close() return has_active except Exception as e: return False def delete_user(username): """ Delete a user from the database. Administrative function for removing user accounts. Args: username (str): Username of the account to delete Returns: bool: True if user was deleted successfully, False otherwise """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] result = users.delete_one({'username': username}) client.close() if result.deleted_count == 0: # Try with different field name client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] result = users.delete_one({'Username': username}) client.close() return result.deleted_count > 0 def update_active_borrowing(username, item_id, status): """ Update a user's active borrowing status. Args: username (str): Username of the user item_id (str): ID of the borrowed item or None if returning status (bool): True if borrowing, False if returning Returns: bool: True if successful, False on error """ try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] result = users.update_one( {'username': username}, {'$set': { 'active_borrowing': status, 'borrowed_item': item_id if status else None }} ) if result.matched_count == 0: result = users.update_one( {'Username': username}, {'$set': { 'active_borrowing': status, 'borrowed_item': item_id if status else None }} ) client.close() return result.modified_count > 0 except Exception as e: return False def get_name(username): """ Retrieve the name that is assosiated with the username. Returns: str: String of name """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'Username': username}) name = user.get("name") return name def get_last_name(username): """ Retrieve the last_name that is assosiated with the username. Returns: str: String of last_name """ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] user = users.find_one({'Username': username}) name = user.get("last_name") return name def get_all_users(): """ Retrieve all users from the database. Administrative function for user management. Returns: list: List of all user documents """ try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] all_users = list(users.find()) client.close() return all_users except Exception as e: return [] def update_password(username, new_password): """ Update a user's password with a new one. Args: username (str): Username of the user new_password (str): New password to set Returns: bool: True if password was updated successfully, False otherwise """ try: if not check_password_strength(new_password): return False client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] # Hash the new password hashed_password = hashing(new_password) # Update the user's password result = users.update_one( {'Username': username}, {'$set': {'Password': hashed_password}} ) client.close() return result.modified_count > 0 except Exception as e: print(f"Error updating password: {e}") return False def update_user_name(username, name, last_name): """ Update a user's name and last name. Args: username (str): Username of the user name (str): New first name last_name (str): New last name Returns: bool: True if updated successfully, False otherwise """ try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] users = db['users'] result = users.update_one( {'Username': username}, {'$set': {'name': name, 'last_name': last_name}} ) client.close() return True except Exception as e: print(f"Error updating user name: {e}") return False