surfaces/core/auth.py
Mikhail Putilovskij 36730ae716 feat: implement core/ and platform/ with full test coverage
- platform/interface.py: PlatformClient Protocol + Pydantic models (User,
  MessageResponse, UserSettings) — no explicit session management, Master
  handles container lifecycle
- platform/mock.py: MockPlatformClient with simulated latency, [MOCK]
  responses, is_new correctly True only on first creation
- core/protocol.py: unified dataclasses for all events and responses
  (IncomingMessage/Command/Callback, OutgoingMessage/UI/Notification,
  AuthFlow, ChatContext, SettingsAction, etc.)
- core/store.py: StateStore Protocol + InMemoryStore (tests) + SQLiteStore
  (prod) with JSON serialization
- core/chat.py: ChatManager — chat metadata (C1/C2/C3), not container
  lifecycle (that's the platform's job)
- core/auth.py: AuthManager — start_flow / confirm / is_authenticated
- core/settings.py: SettingsManager — get/apply with store cache
- core/handler.py: EventDispatcher — registry-based routing with keys
  (command name, action name, attachment type, "*" catch-all)
- core/handlers/: register_all() + start/new/message/callback/settings
  handlers; voice slot falls back to stub text until voice_handler added
- conftest.py: sys.path fix so local platform/ shadows stdlib platform
- docs/api-contract.md: rewritten for Lambda Lab 3.0 container model

46 tests passing, 0 warnings.
2026-03-29 21:42:02 +03:00

52 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# core/auth.py
from __future__ import annotations
import structlog
from core.protocol import AuthFlow
from core.store import StateStore
logger = structlog.get_logger(__name__)
def _to_dict(flow: AuthFlow) -> dict:
return {
"user_id": flow.user_id,
"platform": flow.platform,
"state": flow.state,
"platform_user_id": flow.platform_user_id,
}
def _from_dict(d: dict) -> AuthFlow:
return AuthFlow(
user_id=d["user_id"],
platform=d["platform"],
state=d["state"],
platform_user_id=d.get("platform_user_id"),
)
class AuthManager:
def __init__(self, platform: object, store: StateStore) -> None:
self._store = store
async def start_flow(self, user_id: str, platform: str) -> AuthFlow:
flow = AuthFlow(user_id=user_id, platform=platform, state="pending")
await self._store.set(f"auth:{user_id}", _to_dict(flow))
return flow
async def confirm(self, user_id: str) -> AuthFlow:
"""В моке — автоматическое подтверждение. В реальном SDK — валидация кода."""
stored = await self._store.get(f"auth:{user_id}")
if not stored:
stored = {"user_id": user_id, "platform": "unknown", "state": "pending", "platform_user_id": None}
stored["state"] = "confirmed"
stored["platform_user_id"] = f"plt_{user_id}"
await self._store.set(f"auth:{user_id}", stored)
return _from_dict(stored)
async def is_authenticated(self, user_id: str) -> bool:
stored = await self._store.get(f"auth:{user_id}")
return stored is not None and stored.get("state") == "confirmed"