mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 12:45:42 +02:00
Versamento su API AgID
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Servizio TenantSettings – lettura e aggiornamento configurazione per-tenant.
|
||||
|
||||
Responsabilità:
|
||||
- Upsert della riga settings (crea se non esiste, aggiorna altrimenti)
|
||||
- Cifratura/decifratura AES-256-GCM delle credenziali conservatore
|
||||
- Validazione di consistenza (produzione richiede endpoint + credenziali)
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.exceptions import BadRequestError
|
||||
from app.core.security import decrypt_credential, encrypt_credential
|
||||
from app.models.tenant_settings import TenantSettings
|
||||
from app.schemas.tenant_settings import TenantSettingsResponse, TenantSettingsUpdate
|
||||
|
||||
|
||||
class TenantSettingsService:
|
||||
def __init__(self, db: AsyncSession) -> None:
|
||||
self.db = db
|
||||
|
||||
# ─── Lettura (o creazione defaults) ──────────────────────────────────────
|
||||
|
||||
async def get_or_create(self, tenant_id: uuid.UUID) -> TenantSettings:
|
||||
"""
|
||||
Restituisce le impostazioni del tenant.
|
||||
Se non esistono, crea una riga con i valori di default (mock).
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(TenantSettings).where(TenantSettings.tenant_id == tenant_id)
|
||||
)
|
||||
settings = result.scalar_one_or_none()
|
||||
|
||||
if settings is None:
|
||||
settings = TenantSettings(
|
||||
tenant_id=tenant_id,
|
||||
archival_mode="mock",
|
||||
conservatore_id="mock",
|
||||
)
|
||||
self.db.add(settings)
|
||||
await self.db.flush()
|
||||
|
||||
return settings
|
||||
|
||||
# ─── Aggiornamento ────────────────────────────────────────────────────────
|
||||
|
||||
async def update(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
data: TenantSettingsUpdate,
|
||||
) -> TenantSettings:
|
||||
"""
|
||||
Aggiornamento parziale delle impostazioni.
|
||||
|
||||
Regole:
|
||||
- Il passaggio a 'production' è consentito solo se conservatore_endpoint
|
||||
è già configurato oppure viene fornito nel body corrente.
|
||||
- Le credenziali vengono cifrate prima del salvataggio.
|
||||
- Una stringa vuota "" su username/password cancella la credenziale.
|
||||
"""
|
||||
settings = await self.get_or_create(tenant_id)
|
||||
|
||||
# Applica aggiornamenti campi non-credenziali
|
||||
if data.archival_mode is not None:
|
||||
settings.archival_mode = data.archival_mode
|
||||
|
||||
if data.conservatore_id is not None:
|
||||
settings.conservatore_id = data.conservatore_id
|
||||
|
||||
if data.conservatore_endpoint is not None:
|
||||
settings.conservatore_endpoint = data.conservatore_endpoint or None
|
||||
|
||||
if data.archival_notes is not None:
|
||||
settings.archival_notes = data.archival_notes or None
|
||||
|
||||
# Aggiorna credenziali (cifra se fornite, cancella se stringa vuota)
|
||||
if data.conservatore_username is not None:
|
||||
if data.conservatore_username == "":
|
||||
settings.conservatore_username_enc = None
|
||||
else:
|
||||
settings.conservatore_username_enc = encrypt_credential(
|
||||
data.conservatore_username
|
||||
)
|
||||
|
||||
if data.conservatore_password is not None:
|
||||
if data.conservatore_password == "":
|
||||
settings.conservatore_password_enc = None
|
||||
else:
|
||||
settings.conservatore_password_enc = encrypt_credential(
|
||||
data.conservatore_password
|
||||
)
|
||||
|
||||
# Validazione: produzione richiede endpoint configurato
|
||||
if settings.archival_mode == "production":
|
||||
if not settings.conservatore_endpoint:
|
||||
raise BadRequestError(
|
||||
"La modalità produzione richiede un endpoint conservatore valido. "
|
||||
"Inserisci l'URL prima di attivare la modalità produzione."
|
||||
)
|
||||
|
||||
await self.db.flush()
|
||||
return settings
|
||||
|
||||
# ─── Costruzione response ─────────────────────────────────────────────────
|
||||
|
||||
@staticmethod
|
||||
def to_response(settings: TenantSettings) -> TenantSettingsResponse:
|
||||
"""
|
||||
Converte il modello in DTO di risposta.
|
||||
Le credenziali NON vengono mai esposte in chiaro.
|
||||
"""
|
||||
return TenantSettingsResponse(
|
||||
id=settings.id,
|
||||
tenant_id=settings.tenant_id,
|
||||
archival_mode=settings.archival_mode, # type: ignore[arg-type]
|
||||
conservatore_id=settings.conservatore_id,
|
||||
conservatore_endpoint=settings.conservatore_endpoint,
|
||||
conservatore_username_configured=settings.conservatore_username_enc is not None,
|
||||
conservatore_password_configured=settings.conservatore_password_enc is not None,
|
||||
archival_notes=settings.archival_notes,
|
||||
created_at=settings.created_at,
|
||||
updated_at=settings.updated_at,
|
||||
)
|
||||
|
||||
# ─── Helper per il worker (recupera credenziali decifrate) ───────────────
|
||||
|
||||
async def get_conservatore_credentials(
|
||||
self, tenant_id: uuid.UUID
|
||||
) -> dict:
|
||||
"""
|
||||
Restituisce le credenziali del conservatore decifrate.
|
||||
Usato dal worker per effettuare versamenti.
|
||||
"""
|
||||
settings = await self.get_or_create(tenant_id)
|
||||
return {
|
||||
"mode": settings.archival_mode,
|
||||
"conservatore_id": settings.conservatore_id,
|
||||
"endpoint": settings.conservatore_endpoint,
|
||||
"username": (
|
||||
decrypt_credential(settings.conservatore_username_enc)
|
||||
if settings.conservatore_username_enc
|
||||
else None
|
||||
),
|
||||
"password": (
|
||||
decrypt_credential(settings.conservatore_password_enc)
|
||||
if settings.conservatore_password_enc
|
||||
else None
|
||||
),
|
||||
}
|
||||
Reference in New Issue
Block a user