feat(5.20): US-013 retragere accounts.rar_creds_enc -> per-env + DROP cu garda

Toate citirile pe coloana legacy accounts.rar_creds_enc mutate pe sloturile
per-env (rar_creds_test_enc/rar_creds_prod_enc): worker fallback+keepalive,
are_creds (web) si are_creds_rar (integrare, +are_creds_test/_prod), write-back
API la reactivare, purjare la stergere cont, _get_acasa_context/_fetch_cont_env_state.

Contract API (aditiv): POST /v1/conturi/rar-creds primeste rar_target optional
(test/prod), scrie in slotul corect + activeaza mediul; DELETE primeste ?env
(sterge un slot sau ambele). Documentat in docs/api-rar-contract.md.

DROP cu garda in db.py (schema.sql fara coloana pe DB fresh):
- 6a: eliminat ADD COLUMN rar_creds_enc (fara ping-pong re-ADD dupa DROP)
- 6b: try/except fail-safe (nu crapa boot-ul) + garda sqlite_version >= 3.35
- 6c: re-backfill old->new imediat inainte de assert (ancora globala)
- garda orfane: DROP anulat daca vreun creds legacy nu a aterizat in slot per-env
- backup criptat accounts_rar_creds_enc_backup inainte de DROP
- 6d: verificare prin PRAGMA table_info (NU grep — submissions are aceeasi coloana)
Garda one-way, idempotenta la boot repetat (verificat). submissions.rar_creds_enc
ramane neatinsa.

tests/test_retragere_creds_enc.py: niciun read pe coloana veche, conturi rar-creds
env-aware, are_creds per-env, DROP blocat de garda la lipsa copiere. 9 teste
existente actualizate pe sloturi per-env.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-07-02 21:03:08 +00:00
parent 3d3eb71a1e
commit b1d825e66b
19 changed files with 657 additions and 138 deletions

View File

@@ -131,7 +131,7 @@ def test_roteste_cheie_afisata_o_data(client):
# ============================================================
def test_set_creds_rar_din_sesiune(client):
"""User logat seteaza creds RAR: accounts.rar_creds_enc != NULL, decriptabil."""
"""User logat seteaza creds RAR: slotul per-env (US-013) != NULL, decriptabil."""
acct_id, user_id, _ = _create_account_user("creds@test.com")
_login(client, "creds@test.com", "parolasecreta10")
@@ -147,17 +147,18 @@ def test_set_creds_rar_din_sesiune(client):
assert "succes" in resp.text.lower() or "salvat" in resp.text.lower() or "configurat" in resp.text.lower(), \
f"Mesaj de succes lipsa: {resp.text[:500]}"
# Verifica in DB: rar_creds_enc setat si decriptabil
# Verifica in DB: slotul per-env setat si decriptabil (US-013: rar_creds_enc legacy dropata)
from app.db import get_connection
from app.crypto import decrypt_creds
conn = get_connection()
try:
row = conn.execute(
"SELECT rar_creds_enc FROM accounts WHERE id=?", (acct_id,)
"SELECT rar_creds_test_enc, rar_creds_prod_enc FROM accounts WHERE id=?", (acct_id,)
).fetchone()
assert row is not None
assert row["rar_creds_enc"] is not None, "rar_creds_enc trebuia setat"
creds = decrypt_creds(row["rar_creds_enc"])
enc = row["rar_creds_test_enc"] or row["rar_creds_prod_enc"]
assert enc is not None, "Cel putin un slot per-env trebuia setat (US-013)"
creds = decrypt_creds(enc)
assert creds is not None
assert creds.get("email") == "user@rar.ro"
assert creds.get("password") == "parolaRAR123"
@@ -170,7 +171,7 @@ def test_set_creds_rar_din_sesiune(client):
# ============================================================
def test_creds_alt_cont_neafectat(client):
"""User A seteaza creds -> contul B ramane cu rar_creds_enc NULL."""
"""User A seteaza creds -> contul B ramane fara creds (sloturi per-env NULL, US-013)."""
acct_a, user_a, _ = _create_account_user("userA@test.com")
acct_b, user_b, _ = _create_account_user("userB@test.com")
@@ -184,14 +185,20 @@ def test_creds_alt_cont_neafectat(client):
})
assert resp.status_code == 200
# Verifica: contul A are creds, contul B ramane NULL
# Verifica: contul A are creds in slotul per-env, contul B ramane NULL (US-013)
from app.db import get_connection
conn = get_connection()
try:
row_a = conn.execute("SELECT rar_creds_enc FROM accounts WHERE id=?", (acct_a,)).fetchone()
row_b = conn.execute("SELECT rar_creds_enc FROM accounts WHERE id=?", (acct_b,)).fetchone()
assert row_a["rar_creds_enc"] is not None, "Contul A trebuia sa aiba creds"
assert row_b["rar_creds_enc"] is None, "Contul B nu trebuia atins"
row_a = conn.execute(
"SELECT rar_creds_test_enc, rar_creds_prod_enc FROM accounts WHERE id=?", (acct_a,)
).fetchone()
row_b = conn.execute(
"SELECT rar_creds_test_enc, rar_creds_prod_enc FROM accounts WHERE id=?", (acct_b,)
).fetchone()
enc_a = row_a["rar_creds_test_enc"] or row_a["rar_creds_prod_enc"]
enc_b = row_b["rar_creds_test_enc"] or row_b["rar_creds_prod_enc"]
assert enc_a is not None, "Contul A trebuia sa aiba creds in slotul per-env"
assert enc_b is None, "Contul B nu trebuia atins"
finally:
conn.close()