# Clean Architecture Design - AccountForger ## Übersicht Diese Dokumentation beschreibt die saubere Architektur für das AccountForger-System mit Fokus auf das Fingerprint-Management und die Login-Funktionalität mit gespeicherten Fingerprints. ## Architektur-Schichten ### 1. Domain Layer (Innerster Kreis) **Keine Abhängigkeiten nach außen!** ``` domain/ ├── entities/ │ ├── account.py # Account Entity │ ├── browser_fingerprint.py # Fingerprint Entity │ └── browser_session.py # Session Entity ├── value_objects/ │ ├── fingerprint_id.py # Eindeutige Fingerprint-ID │ ├── account_id.py # Eindeutige Account-ID │ └── session_data.py # Session-Daten (Cookies, Storage) └── repositories/ # Interfaces (Abstrakte Klassen) ├── fingerprint_repository.py ├── account_repository.py └── session_repository.py ``` ### 2. Application Layer **Orchestriert Use Cases, kennt Domain** ``` application/ ├── use_cases/ │ ├── create_account/ │ │ ├── create_account_use_case.py │ │ ├── create_account_dto.py │ │ └── create_account_presenter.py │ ├── login_account/ │ │ ├── login_with_fingerprint_use_case.py │ │ ├── login_dto.py │ │ └── login_presenter.py │ └── manage_fingerprint/ │ ├── generate_fingerprint_use_case.py │ ├── save_fingerprint_use_case.py │ └── load_fingerprint_use_case.py └── services/ ├── fingerprint_manager.py # Orchestriert Fingerprint-Operationen └── session_manager.py # Verwaltet Browser-Sessions ``` ### 3. Infrastructure Layer **Implementiert Interfaces aus Domain** ``` infrastructure/ ├── persistence/ │ ├── sqlite/ │ │ ├── sqlite_fingerprint_repository.py │ │ ├── sqlite_account_repository.py │ │ └── sqlite_session_repository.py │ └── migrations/ │ └── fingerprint_schema.sql ├── browser/ │ ├── playwright_adapter.py # Adapter für Playwright │ ├── fingerprint_injector.py # Injiziert Fingerprints in Browser │ └── protection_service.py # Browser-Schutz └── external/ ├── proxy_service.py └── email_service.py ``` ### 4. Presentation Layer **UI und Controller** ``` presentation/ ├── controllers/ │ ├── account_controller.py │ └── fingerprint_controller.py └── views/ ├── account_view.py └── login_view.py ``` ## Fingerprint-System Design ### Fingerprint Entity (Kern-Domain) ```python # domain/entities/browser_fingerprint.py from dataclasses import dataclass from typing import Optional import uuid @dataclass(frozen=True) # Immutable! class FingerprintId: value: str @classmethod def generate(cls) -> 'FingerprintId': return cls(str(uuid.uuid4())) @dataclass class BrowserFingerprint: """Immutable Fingerprint Entity - Kern der Domain""" id: FingerprintId canvas_seed: int webgl_vendor: str webgl_renderer: str audio_context_params: dict navigator_properties: dict hardware_config: dict timezone: str fonts: list[str] def to_dict(self) -> dict: """Serialisierung für Persistierung""" return { 'id': self.id.value, 'canvas_seed': self.canvas_seed, # ... weitere Felder } @classmethod def from_dict(cls, data: dict) -> 'BrowserFingerprint': """Deserialisierung aus Persistierung""" return cls( id=FingerprintId(data['id']), canvas_seed=data['canvas_seed'], # ... weitere Felder ) ``` ### Fingerprint-Account-Session Verknüpfung ```python # domain/entities/account.py @dataclass class Account: id: AccountId username: str platform: str fingerprint_id: FingerprintId # Verknüpfung! created_at: datetime # domain/entities/browser_session.py @dataclass class BrowserSession: id: SessionId account_id: AccountId fingerprint_id: FingerprintId # Gleicher Fingerprint! cookies: str # Encrypted local_storage: str # Encrypted session_storage: str # Encrypted last_used: datetime is_valid: bool ``` ### Use Case: Login mit gespeichertem Fingerprint ```python # application/use_cases/login_account/login_with_fingerprint_use_case.py class LoginWithFingerprintUseCase: def __init__(self, account_repo: IAccountRepository, fingerprint_repo: IFingerprintRepository, session_repo: ISessionRepository, browser_service: IBrowserService): self.account_repo = account_repo self.fingerprint_repo = fingerprint_repo self.session_repo = session_repo self.browser_service = browser_service def execute(self, account_id: str) -> LoginResult: # 1. Account laden account = self.account_repo.find_by_id(AccountId(account_id)) if not account: return LoginResult.failure("Account nicht gefunden") # 2. Fingerprint laden fingerprint = self.fingerprint_repo.find_by_id(account.fingerprint_id) if not fingerprint: return LoginResult.failure("Fingerprint nicht gefunden") # 3. Session laden session = self.session_repo.find_by_account_id(account.id) if not session or not session.is_valid: return LoginResult.failure("Keine gültige Session") # 4. Browser mit Fingerprint starten browser = self.browser_service.create_with_fingerprint(fingerprint) # 5. Session wiederherstellen browser.restore_session(session) # 6. Login verifizieren if browser.verify_login(account.platform): return LoginResult.success(browser) else: return LoginResult.failure("Login fehlgeschlagen") ``` ### Repository Pattern (Clean!) ```python # domain/repositories/fingerprint_repository.py from abc import ABC, abstractmethod class IFingerprintRepository(ABC): @abstractmethod def save(self, fingerprint: BrowserFingerprint) -> None: pass @abstractmethod def find_by_id(self, id: FingerprintId) -> Optional[BrowserFingerprint]: pass @abstractmethod def find_by_account_id(self, account_id: AccountId) -> Optional[BrowserFingerprint]: pass # infrastructure/persistence/sqlite/sqlite_fingerprint_repository.py class SqliteFingerprintRepository(IFingerprintRepository): def save(self, fingerprint: BrowserFingerprint) -> None: # SQL Implementation query = "INSERT OR REPLACE INTO fingerprints ..." # Nur primitive Typen in DB! data = fingerprint.to_dict() self.db.execute(query, data) def find_by_id(self, id: FingerprintId) -> Optional[BrowserFingerprint]: query = "SELECT * FROM fingerprints WHERE id = ?" row = self.db.fetchone(query, [id.value]) return BrowserFingerprint.from_dict(row) if row else None ``` ### Dependency Injection Container ```python # infrastructure/container.py class Container: def __init__(self): # Repositories self._fingerprint_repo = SqliteFingerprintRepository() self._account_repo = SqliteAccountRepository() self._session_repo = SqliteSessionRepository() # Services self._browser_service = PlaywrightBrowserService() # Use Cases self._login_use_case = LoginWithFingerprintUseCase( self._account_repo, self._fingerprint_repo, self._session_repo, self._browser_service ) @property def login_use_case(self) -> LoginWithFingerprintUseCase: return self._login_use_case ``` ## Datenbank-Schema ```sql -- Fingerprints Tabelle CREATE TABLE fingerprints ( id TEXT PRIMARY KEY, canvas_seed INTEGER NOT NULL, webgl_vendor TEXT NOT NULL, webgl_renderer TEXT NOT NULL, audio_context_params TEXT NOT NULL, -- JSON navigator_properties TEXT NOT NULL, -- JSON hardware_config TEXT NOT NULL, -- JSON timezone TEXT NOT NULL, fonts TEXT NOT NULL, -- JSON Array created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Accounts Tabelle CREATE TABLE accounts ( id TEXT PRIMARY KEY, username TEXT NOT NULL, platform TEXT NOT NULL, fingerprint_id TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (fingerprint_id) REFERENCES fingerprints(id) ); -- Sessions Tabelle CREATE TABLE browser_sessions ( id TEXT PRIMARY KEY, account_id TEXT NOT NULL, fingerprint_id TEXT NOT NULL, cookies TEXT NOT NULL, -- Encrypted local_storage TEXT, -- Encrypted session_storage TEXT, -- Encrypted last_used TIMESTAMP, is_valid BOOLEAN DEFAULT 1, FOREIGN KEY (account_id) REFERENCES accounts(id), FOREIGN KEY (fingerprint_id) REFERENCES fingerprints(id) ); -- Index für Performance CREATE INDEX idx_accounts_fingerprint ON accounts(fingerprint_id); CREATE INDEX idx_sessions_account ON browser_sessions(account_id); ``` ## Vorteile dieser Architektur 1. **Testbarkeit**: Jede Schicht ist isoliert testbar 2. **Flexibilität**: Repositories können ausgetauscht werden (SQLite → PostgreSQL) 3. **Klarheit**: Klare Verantwortlichkeiten pro Schicht 4. **Wartbarkeit**: Änderungen sind lokal begrenzt 5. **Fingerprint-Konsistenz**: Ein Account = Ein Fingerprint = Konsistente Sessions ## Login-Flow mit Fingerprint 1. User wählt Account aus Liste 2. System lädt Account mit verknüpftem Fingerprint 3. Browser wird mit exakt diesem Fingerprint gestartet 4. Gespeicherte Session (Cookies, Storage) wird geladen 5. Browser navigiert zur Plattform 6. Session ist wiederhergestellt = User ist eingeloggt ## Beispiel-Verwendung ```python # In der Presentation Layer container = Container() # Login mit gespeichertem Fingerprint result = container.login_use_case.execute(account_id="abc-123") if result.success: browser = result.browser # User ist jetzt eingeloggt mit dem gleichen Fingerprint else: print(f"Login fehlgeschlagen: {result.error}") ``` Diese Architektur stellt sicher, dass: - Fingerprints konsistent bleiben - Sessions zuverlässig wiederhergestellt werden - Der Code wartbar und erweiterbar bleibt - Keine zirkulären Abhängigkeiten entstehen