"""Tech user management - JSON file CRUD with PIN auth.""" import json import os import secrets import string from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash from config import Config TECH_USERS_FILE = os.environ.get('TECH_USERS_FILE', '/app/data/tech_users.json') def _load(): """Load tech users from JSON file.""" if not os.path.exists(TECH_USERS_FILE): return {} with open(TECH_USERS_FILE, 'r') as f: return json.load(f) def _save(data): """Save tech users to JSON file.""" os.makedirs(os.path.dirname(TECH_USERS_FILE), exist_ok=True) with open(TECH_USERS_FILE, 'w') as f: json.dump(data, f, indent=2) def generate_nc_password(length=16): """Generate a random password for Nextcloud account.""" alphabet = string.ascii_letters + string.digits return ''.join(secrets.choice(alphabet) for _ in range(length)) def create(username, display_name, pin, created_by): """Create a new tech user. Returns the record (with nc_password) or raises.""" data = _load() if username in data: raise ValueError(f'Tech user "{username}" already exists') nc_password = generate_nc_password() data[username] = { 'display_name': display_name, 'pin_hash': generate_password_hash(pin), 'nc_password': nc_password, 'created_at': datetime.utcnow().isoformat(), 'created_by': created_by, 'enabled': True, } _save(data) return data[username] def verify_pin(username, pin): """Verify a tech user's PIN. Returns the user record or None.""" data = _load() user = data.get(username) if not user: return None if not user.get('enabled', True): return None if not check_password_hash(user['pin_hash'], pin): return None return user def list_all(): """Return all tech users (with nc_password visible).""" return _load() def get(username): """Get a single tech user record.""" return _load().get(username) def update(username, **fields): """Update a tech user. Supported fields: display_name, pin, enabled.""" data = _load() user = data.get(username) if not user: raise ValueError(f'Tech user "{username}" not found') if 'display_name' in fields: user['display_name'] = fields['display_name'] if 'pin' in fields: user['pin_hash'] = generate_password_hash(fields['pin']) if 'enabled' in fields: user['enabled'] = fields['enabled'] data[username] = user _save(data) return user def reset_nc_password(username): """Generate a new NC password and save it. Returns the new password.""" data = _load() user = data.get(username) if not user: raise ValueError(f'Tech user "{username}" not found') new_password = generate_nc_password() user['nc_password'] = new_password data[username] = user _save(data) return new_password def delete(username): """Delete a tech user from JSON.""" data = _load() if username not in data: raise ValueError(f'Tech user "{username}" not found') del data[username] _save(data)