from flask import current_app from flask_login import UserMixin import json import os from datetime import datetime import pytz from app import bcrypt class User(UserMixin): """Clase de usuario para Flask-Login.""" def __init__(self, username, data): self.id = username self.username = username self.nombre = data.get('nombre', '') self.email = data.get('email', '') self.password_hash = data.get('password_hash', '') self.nivel = data.get('nivel', 0) self.idioma = data.get('idioma', 'es') self.fecha_caducidad = data.get('fecha_caducidad') self.empresa = data.get('empresa', '') self.estado = data.get('estado', 'inactivo') self.ultimo_acceso = data.get('ultimo_acceso') def verify_password(self, password): """Verificar contraseña.""" return bcrypt.check_password_hash(self.password_hash, password) def is_active(self): """Verificar si el usuario está activo.""" return self.estado == 'activo' def is_expired(self): """Verificar si la cuenta del usuario ha expirado.""" if not self.fecha_caducidad: return False now = datetime.now(pytz.UTC) expiry_date = datetime.fromisoformat(self.fecha_caducidad.replace('Z', '+00:00')) return now > expiry_date def has_permission(self, required_level): """Verificar si el usuario tiene el nivel de permiso requerido.""" return self.nivel >= required_level def to_dict(self): """Convertir usuario a diccionario.""" return { 'nombre': self.nombre, 'username': self.username, 'email': self.email, 'password_hash': self.password_hash, 'nivel': self.nivel, 'idioma': self.idioma, 'fecha_caducidad': self.fecha_caducidad, 'empresa': self.empresa, 'estado': self.estado, 'ultimo_acceso': self.ultimo_acceso } def authenticate_user(username, password): """Autenticar usuario con nombre de usuario y contraseña.""" user = get_user_by_username(username) if user and user.verify_password(password) and user.is_active() and not user.is_expired(): # Actualizar último acceso update_last_access(username) return user return None def get_user_by_username(username): """Obtener usuario por nombre de usuario.""" users = load_users() if username in users: return User(username, users[username]) return None def get_user_by_id(user_id): """Cargar usuario por ID (username) para Flask-Login.""" return get_user_by_username(user_id) def update_last_access(username): """Actualizar el timestamp de último acceso.""" users = load_users() if username in users: users[username]['ultimo_acceso'] = datetime.now(pytz.UTC).isoformat() save_users(users) def create_user(username, nombre, email, password, nivel=0, idioma='es', fecha_caducidad=None, empresa='', estado='activo'): """Crear un nuevo usuario.""" users = load_users() # Verificar si ya existe el usuario if username in users: return False, "El nombre de usuario ya existe." # Generar hash para la contraseña password_hash = bcrypt.generate_password_hash(password).decode('utf-8') # Crear entrada de usuario users[username] = { 'nombre': nombre, 'username': username, 'email': email, 'password_hash': password_hash, 'nivel': nivel, 'idioma': idioma, 'fecha_caducidad': fecha_caducidad, 'empresa': empresa, 'estado': estado, 'ultimo_acceso': datetime.now(pytz.UTC).isoformat() } # Guardar usuarios save_users(users) return True, "Usuario creado correctamente." def update_user(username, data): """Actualizar datos de usuario.""" users = load_users() # Verificar si existe el usuario if username not in users: return False, "El usuario no existe." # Actualizar campos (excepto password si no se especifica) for key, value in data.items(): if key != 'password': # Contraseña se maneja separado users[username][key] = value # Actualizar contraseña si se proporciona if 'password' in data and data['password']: users[username]['password_hash'] = bcrypt.generate_password_hash( data['password']).decode('utf-8') # Guardar usuarios save_users(users) return True, "Usuario actualizado correctamente." def delete_user(username): """Eliminar usuario.""" users = load_users() # Verificar si existe el usuario if username not in users: return False, "El usuario no existe." # Eliminar usuario del users[username] # Guardar usuarios save_users(users) return True, "Usuario eliminado correctamente." def load_users(): """Cargar usuarios desde archivo JSON.""" storage_path = current_app.config['STORAGE_PATH'] users_file = os.path.join(storage_path, 'users', 'users.json') try: with open(users_file, 'r', encoding='utf-8') as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return {} def save_users(users): """Guardar usuarios en archivo JSON.""" storage_path = current_app.config['STORAGE_PATH'] users_file = os.path.join(storage_path, 'users', 'users.json') with open(users_file, 'w', encoding='utf-8') as f: json.dump(users, f, ensure_ascii=False, indent=2) def get_all_users(): """Obtener lista de todos los usuarios.""" users = load_users() return [User(username, data) for username, data in users.items()] def initialize_admin_user(): """Crear usuario administrador si no existe.""" users = load_users() if 'admin' not in users: create_user( username='admin', nombre='Administrador', email='admin@example.com', password='admin123', # Cambiar en producción nivel=9999, estado='activo' ) current_app.logger.info('Usuario administrador creado.')