160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
"""
|
|
AUTARCH User Profile Manager
|
|
Handles user identity, preferences, and session management.
|
|
|
|
On first launch, creates a default profile in the user data directory.
|
|
Supports multiple users for shared deployments.
|
|
|
|
The profile stores non-sensitive user info (username, display name, role, etc.)
|
|
Sensitive data (API keys, passwords) goes in the keystore.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Dict, Optional
|
|
|
|
_log = logging.getLogger('autarch.user_profile')
|
|
|
|
|
|
class UserProfile:
|
|
"""User profile and preferences."""
|
|
|
|
def __init__(self, profile_path: Path = None):
|
|
from core.paths import get_user_profile_path
|
|
self._path = profile_path or get_user_profile_path()
|
|
self._data: Dict = {}
|
|
self._load()
|
|
|
|
def _load(self):
|
|
"""Load profile from disk, or create default."""
|
|
if self._path.exists():
|
|
try:
|
|
with open(self._path) as f:
|
|
self._data = json.load(f)
|
|
_log.info(f'Loaded profile for {self._data.get("username", "unknown")}')
|
|
return
|
|
except Exception as e:
|
|
_log.error(f'Failed to load profile: {e}')
|
|
|
|
# Create default profile
|
|
self._data = self._create_default()
|
|
self._save()
|
|
_log.info('Created default user profile')
|
|
|
|
def _create_default(self) -> Dict:
|
|
"""Create the default user profile."""
|
|
username = os.getenv('USER', os.getenv('USERNAME', 'operator'))
|
|
return {
|
|
'username': username,
|
|
'display_name': username.capitalize(),
|
|
'role': 'admin',
|
|
'created': int(time.time()),
|
|
'last_login': int(time.time()),
|
|
'preferences': {
|
|
'theme': 'dark',
|
|
'llm_backend': 'local',
|
|
'hal_auto_analyze': True,
|
|
'hal_max_steps': 200,
|
|
'verbose': False,
|
|
'default_scan_target': 'local', # 'local' scans user device, 'server' scans server
|
|
'chat_memory_enabled': True,
|
|
},
|
|
'session': {
|
|
'last_module': '',
|
|
'last_target': '',
|
|
'last_scan_type': '',
|
|
},
|
|
}
|
|
|
|
def _save(self):
|
|
"""Save profile to disk."""
|
|
try:
|
|
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(self._path, 'w') as f:
|
|
json.dump(self._data, f, indent=2)
|
|
except Exception as e:
|
|
_log.error(f'Failed to save profile: {e}')
|
|
|
|
@property
|
|
def username(self) -> str:
|
|
return self._data.get('username', 'default')
|
|
|
|
@property
|
|
def display_name(self) -> str:
|
|
return self._data.get('display_name', self.username)
|
|
|
|
@property
|
|
def role(self) -> str:
|
|
return self._data.get('role', 'user')
|
|
|
|
def get_preference(self, key: str, default=None):
|
|
"""Get a user preference value."""
|
|
return self._data.get('preferences', {}).get(key, default)
|
|
|
|
def set_preference(self, key: str, value):
|
|
"""Set a user preference and save."""
|
|
if 'preferences' not in self._data:
|
|
self._data['preferences'] = {}
|
|
self._data['preferences'][key] = value
|
|
self._save()
|
|
|
|
def get_session(self, key: str, default='') -> str:
|
|
"""Get a session value (non-persistent state hints)."""
|
|
return self._data.get('session', {}).get(key, default)
|
|
|
|
def set_session(self, key: str, value: str):
|
|
"""Set a session value."""
|
|
if 'session' not in self._data:
|
|
self._data['session'] = {}
|
|
self._data['session'][key] = value
|
|
self._save()
|
|
|
|
def update_last_login(self):
|
|
"""Record login timestamp."""
|
|
self._data['last_login'] = int(time.time())
|
|
self._save()
|
|
|
|
def to_dict(self) -> Dict:
|
|
"""Export profile as a dict (for API/display)."""
|
|
return {
|
|
'username': self.username,
|
|
'display_name': self.display_name,
|
|
'role': self.role,
|
|
'created': self._data.get('created', 0),
|
|
'last_login': self._data.get('last_login', 0),
|
|
'preferences': self._data.get('preferences', {}),
|
|
}
|
|
|
|
|
|
# Singleton
|
|
_profile_instance: Optional[UserProfile] = None
|
|
|
|
|
|
def get_profile() -> UserProfile:
|
|
"""Get the global user profile instance."""
|
|
global _profile_instance
|
|
if _profile_instance is None:
|
|
_profile_instance = UserProfile()
|
|
return _profile_instance
|
|
|
|
|
|
def init_profile_on_launch():
|
|
"""Called at app startup to ensure profile exists and update login time."""
|
|
profile = get_profile()
|
|
profile.update_last_login()
|
|
|
|
# Migrate old vault secrets to new keystore if needed
|
|
from core.paths import get_keystore_path
|
|
ks_path = get_keystore_path()
|
|
if not ks_path.exists():
|
|
from core.keystore import get_keystore
|
|
ks = get_keystore()
|
|
migrated = ks.migrate_from_vault()
|
|
if migrated:
|
|
_log.info(f'Migrated {migrated} secrets from vault to keystore on first run')
|
|
|
|
return profile
|