From 6c426bc274401ac70e1cdabdba4439b699becb92 Mon Sep 17 00:00:00 2001 From: Apple Date: Sat, 10 Jan 2026 10:43:14 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=90=20Auth:=20=D0=B1=D0=B0=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=20=D1=80=D0=B5=D0=B0=D0=BB=D1=96=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D1=96=D1=8F=20JWT=20=D0=B4=D0=BB=D1=8F=20Memory=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JWT middleware для FastAPI - Генерація/перевірка JWT токенів - Скрипти для генерації Qdrant API keys - Скрипти для генерації NATS operator JWT - План реалізації Auth TODO: Додати JWT до endpoints, NATS nkeys config, Qdrant API key config --- .../auth/AUTH-IMPLEMENTATION-PLAN.md | 72 +++++++++++ infrastructure/auth/nats/generate-operator.sh | 38 ++++++ infrastructure/auth/qdrant/generate-keys.sh | 22 ++++ infrastructure/matrix-gateway/test_gateway.py | 118 ++++++++++++++++++ services/memory-service/app/auth.py | 58 +++++++++ services/memory-service/app/config.py | 5 + 6 files changed, 313 insertions(+) create mode 100644 infrastructure/auth/AUTH-IMPLEMENTATION-PLAN.md create mode 100755 infrastructure/auth/nats/generate-operator.sh create mode 100755 infrastructure/auth/qdrant/generate-keys.sh create mode 100755 infrastructure/matrix-gateway/test_gateway.py create mode 100644 services/memory-service/app/auth.py diff --git a/infrastructure/auth/AUTH-IMPLEMENTATION-PLAN.md b/infrastructure/auth/AUTH-IMPLEMENTATION-PLAN.md new file mode 100644 index 00000000..d18bef93 --- /dev/null +++ b/infrastructure/auth/AUTH-IMPLEMENTATION-PLAN.md @@ -0,0 +1,72 @@ +# 🔐 План реалізації Auth + +**Дата:** 2026-01-10 +**Версія:** 1.0.0 + +--- + +## 📋 Компоненти для Auth + +### 1. NATS (nkeys) + +**Статус:** TODO +**Пріоритет:** Високий + +**Що робити:** +- Генерація NATS operator JWT +- Створення system account +- Створення user accounts для сервісів +- Оновлення NATS конфігурації з auth + +**Файли:** +- `infrastructure/auth/nats/generate-operator.sh` — генерація operator +- `infrastructure/auth/nats/create-accounts.sh` — створення accounts +- `infrastructure/kubernetes/nats/auth-secrets.yaml` — Secrets для JWT + +--- + +### 2. Memory Service (JWT) + +**Статус:** TODO +**Пріоритет:** Високий + +**Що робити:** +- Додати JWT middleware до FastAPI +- Генерація JWT токенів для сервісів +- Перевірка JWT в Memory Service endpoints +- Створення service-to-service токенів + +**Файли:** +- `services/memory-service/app/auth.py` — JWT middleware +- `services/memory-service/app/jwt_utils.py` — генерація/перевірка JWT +- `infrastructure/kubernetes/apps/memory-service/auth-secrets.yaml` — Secrets + +--- + +### 3. Qdrant (API key) + +**Статус:** TODO +**Пріоритет:** Середній + +**Що робити:** +- Увімкнути API key auth в Qdrant +- Генерація API ключів для кожного сервісу +- Оновлення Memory Service для використання API key +- Оновлення worker-daemon для використання API key + +**Файли:** +- `infrastructure/kubernetes/apps/qdrant/config.yaml` — Qdrant config з auth +- `infrastructure/auth/qdrant/generate-keys.sh` — генерація ключів +- `infrastructure/kubernetes/apps/qdrant/auth-secrets.yaml` — Secrets + +--- + +## 🔄 Порядок реалізації + +1. **Memory Service JWT** (найпростіше, найважливіше) +2. **NATS nkeys** (середня складність, важливо для безпеки) +3. **Qdrant API key** (найпростіше, але менш критично) + +--- + +*Документ створено: 2026-01-10 19:30 CET* diff --git a/infrastructure/auth/nats/generate-operator.sh b/infrastructure/auth/nats/generate-operator.sh new file mode 100755 index 00000000..9c04b885 --- /dev/null +++ b/infrastructure/auth/nats/generate-operator.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Генерація NATS operator JWT та accounts + +set -e + +echo "🔑 Генерація NATS operator JWT..." + +# Перевірка наявності nsc (NATS CLI для управління accounts) +if ! command -v nsc &> /dev/null; then + echo "⚠️ nsc не встановлено. Встановіть: https://github.com/nats-io/natscli" + echo " Або використайте Docker: docker run -it --rm natsio/nats-box" + exit 1 +fi + +# Створення operator +OPERATOR_NAME="DAARION" +echo "Створення operator: $OPERATOR_NAME" +nsc add operator "$OPERATOR_NAME" + +# Створення system account +SYSTEM_ACCOUNT="SYSTEM" +echo "Створення system account: $SYSTEM_ACCOUNT" +nsc add account "$SYSTEM_ACCOUNT" + +# Створення user accounts для сервісів +echo "Створення user accounts..." +nsc add user --account "$SYSTEM_ACCOUNT" memory-service +nsc add user --account "$SYSTEM_ACCOUNT" worker-daemon +nsc add user --account "$SYSTEM_ACCOUNT" matrix-gateway + +echo "" +echo "✅ Operator та accounts створено!" +echo "" +echo "JWT файли знаходяться в: ~/.nsc/" +echo " Operator JWT: ~/.nsc/nats/$OPERATOR_NAME/$OPERATOR_NAME.jwt" +echo " System Account JWT: ~/.nsc/nats/$OPERATOR_NAME/accounts/$SYSTEM_ACCOUNT/$SYSTEM_ACCOUNT.jwt" +echo "" +echo "⚠️ Збережіть ці JWT в Vault або K8s Secrets!" diff --git a/infrastructure/auth/qdrant/generate-keys.sh b/infrastructure/auth/qdrant/generate-keys.sh new file mode 100755 index 00000000..7766eaa1 --- /dev/null +++ b/infrastructure/auth/qdrant/generate-keys.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Генерація API ключів для Qdrant + +set -e + +echo "🔑 Генерація API ключів для Qdrant..." + +# Генерація випадкових ключів +MEMORY_SERVICE_KEY=$(openssl rand -hex 32) +WORKER_DAEMON_KEY=$(openssl rand -hex 32) +MATRIX_GATEWAY_KEY=$(openssl rand -hex 32) + +echo "" +echo "✅ Згенеровано API ключі:" +echo " Memory Service: $MEMORY_SERVICE_KEY" +echo " Worker Daemon: $WORKER_DAEMON_KEY" +echo " Matrix Gateway: $MATRIX_GATEWAY_KEY" +echo "" +echo "⚠️ Збережіть ці ключі в Vault або K8s Secrets!" +echo "" +echo "Для Qdrant config додайте:" +echo " QDRANT__SERVICE__API_KEY: $MEMORY_SERVICE_KEY" diff --git a/infrastructure/matrix-gateway/test_gateway.py b/infrastructure/matrix-gateway/test_gateway.py new file mode 100755 index 00000000..95c50c30 --- /dev/null +++ b/infrastructure/matrix-gateway/test_gateway.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +""" +Тестовий скрипт для Matrix Gateway +Перевірка базової функціональності без реального Matrix connection +""" + +import asyncio +import json +from gateway.job_creator import JobCreator +from gateway.nats_publisher import NATSPublisher + + +async def test_job_creation(): + """Тест створення job""" + print("🧪 Тест створення job...") + + # Mock NATS publisher + class MockNATSPublisher: + async def connect(self): + pass + async def disconnect(self): + pass + + publisher = MockNATSPublisher() + creator = JobCreator(publisher) + + # Тест команди embed + command = { + "type": "embed", + "priority": "online", + "input": { + "text": ["Привіт, це тестовий текст"], + "model": "cohere/embed-multilingual-v3.0", + "dims": 1024 + } + } + + job = await creator.create_job(command) + + print(f"✅ Job створено:") + print(f" Job ID: {job['job_id']}") + print(f" Type: {job['type']}") + print(f" Priority: {job['priority']}") + print(f" Idempotency Key: {job['idempotency_key']}") + print(f" Requirements: {job['requirements']}") + + # Перевірка структури + assert "job_id" in job + assert "idempotency_key" in job + assert job["idempotency_key"].startswith("sha256:") + assert job["type"] == "embed" + assert job["priority"] == "online" + + print("✅ Всі перевірки пройдено!") + return True + + +async def test_command_parsing(): + """Тест парсингу команд""" + print("\n🧪 Тест парсингу команд...") + + from gateway.main import MatrixGateway + + gateway = MatrixGateway() + + # Тест !embed + command = gateway._parse_command("!embed Привіт, це тест") + assert command is not None + assert command["type"] == "embed" + assert command["priority"] == "online" + assert command["input"]["text"] == ["Привіт, це тест"] + print("✅ !embed команда парситься правильно") + + # Тест !retrieve + command = gateway._parse_command("!retrieve пошуковий запит") + assert command is not None + assert command["type"] == "retrieve" + assert command["priority"] == "online" + print("✅ !retrieve команда парситься правильно") + + # Тест !summarize + command = gateway._parse_command("!summarize thread-123") + assert command is not None + assert command["type"] == "summarize" + assert command["priority"] == "offline" + print("✅ !summarize команда парситься правильно") + + # Тест невірної команди + command = gateway._parse_command("!unknown команда") + assert command is None + print("✅ Невірна команда правильно відхиляється") + + print("✅ Всі перевірки парсингу пройдено!") + return True + + +async def main(): + """Головна функція тестування""" + print("🚀 Тестування Matrix Gateway\n") + + try: + await test_job_creation() + await test_command_parsing() + + print("\n✅ Всі тести пройдено успішно!") + return 0 + except AssertionError as e: + print(f"\n❌ Тест не пройдено: {e}") + return 1 + except Exception as e: + print(f"\n❌ Помилка: {e}") + import traceback + traceback.print_exc() + return 1 + + +if __name__ == "__main__": + exit(asyncio.run(main())) diff --git a/services/memory-service/app/auth.py b/services/memory-service/app/auth.py new file mode 100644 index 00000000..7d7b3789 --- /dev/null +++ b/services/memory-service/app/auth.py @@ -0,0 +1,58 @@ +""" +JWT Authentication для Memory Service +""" + +import os +import jwt +import time +from typing import Optional +from fastapi import HTTPException, Security +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from app.config import get_settings + +settings = get_settings() + +# JWT settings +JWT_SECRET = settings.jwt_secret or os.getenv("MEMORY_JWT_SECRET", "change-me-in-production") +JWT_ALGORITHM = settings.jwt_algorithm +JWT_EXPIRATION = settings.jwt_expiration + +security = HTTPBearer() + + +def generate_jwt_token(service_name: str, permissions: list = None) -> str: + """Генерація JWT токену для сервісу""" + payload = { + "service": service_name, + "permissions": permissions or ["read", "write"], + "iat": int(time.time()), + "exp": int(time.time()) + JWT_EXPIRATION + } + return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) + + +def verify_jwt_token(token: str) -> dict: + """Перевірка JWT токену""" + try: + payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) + return payload + except jwt.ExpiredSignatureError: + raise HTTPException(status_code=401, detail="Token expired") + except jwt.InvalidTokenError: + raise HTTPException(status_code=401, detail="Invalid token") + + +async def get_current_service(credentials: HTTPAuthorizationCredentials = Security(security)) -> dict: + """Dependency для отримання поточного сервісу з JWT""" + token = credentials.credentials + payload = verify_jwt_token(token) + return payload + + +def require_permission(permission: str): + """Decorator для перевірки прав доступу""" + async def permission_checker(service: dict = Security(get_current_service)): + if permission not in service.get("permissions", []): + raise HTTPException(status_code=403, detail=f"Permission '{permission}' required") + return service + return permission_checker diff --git a/services/memory-service/app/config.py b/services/memory-service/app/config.py index 5af632b2..5dc34bdc 100644 --- a/services/memory-service/app/config.py +++ b/services/memory-service/app/config.py @@ -46,6 +46,11 @@ class Settings(BaseSettings): memory_confirm_boost: float = 0.1 memory_reject_penalty: float = 0.3 + # JWT Auth + jwt_secret: str = "" # Must be set via MEMORY_JWT_SECRET env var or Vault + jwt_algorithm: str = "HS256" + jwt_expiration: int = 3600 # 1 година + class Config: env_prefix = "MEMORY_" env_file = ".env"