feat(service-auto): săpt 3-phase2 — toate ipotezele confirmate + modul funcțional
Backend: - service_auto module complet: router, service, schemas, 5 teste suites (22/22 passed) - 5 endpoints: GET /ping, /firme, /tip-deviz, /masini, POST /comenzi - SP_CREEAZA_COMANDA_PROTOTIP creat în MARIUSM_AUTO (VALID, 5.9ms) - oracle_pool.py: session_callback backward-compat patch - ROA_WEB user: grants SP-only confirmate (H3), mariusm_test pool switchat - pyproject.toml: integration pytest marker înregistrat Frontend: - ComandaNoua.vue: date reale din Oracle (firme/tip-deviz/masini), nu hardcodate - src/modules/service-auto/services/api.js: axios service cu Bearer token - src/router/index.js: rută /service-auto/comanda-noua Docs: - decision-log.md: verdict MERGE, toate 6 ipoteze CONFIRMED - learnings.md: 7 patterns reutilizabile - grants-audit.md: arhitectura multi-tenant + proxy auth analysis + V_NOM_FIRME loop - template-modul-oracle.md: rețetă completă pentru module Oracle noi - TODO-phase2.md: 7 items concrete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
161
backend/modules/service_auto/tests/test_grants_integration.py
Normal file
161
backend/modules/service_auto/tests/test_grants_integration.py
Normal file
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
Integration tests — Hypothesis #3: ROA_WEB grant-scoped access.
|
||||
|
||||
Proves:
|
||||
- ROA_WEB cannot INSERT directly into MARIUSM_AUTO.NOM_LUCRARI → ORA-01031 or ORA-00942
|
||||
- ROA_WEB cannot SELECT directly from MARIUSM_AUTO.NOM_LUCRARI → ORA-00942
|
||||
- ROA_WEB CAN execute SP_CREEAZA_COMANDA_PROTOTIP via EXECUTE grant → no privilege error
|
||||
|
||||
All tests skip gracefully when:
|
||||
- backend/secrets/roa_web.oracle_pass is absent (user not yet created)
|
||||
- ORA-01017 on connect (user doesn't exist / wrong password)
|
||||
|
||||
Reference: docs/service-auto/grants-audit.md §3.1
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import oracledb
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Oracle connection constants (MARIUSM_AUTO on central server)
|
||||
# ---------------------------------------------------------------------------
|
||||
_HOST = "10.0.20.121"
|
||||
_PORT = 1521
|
||||
_SERVICE_NAME = "ROA"
|
||||
_ROA_WEB_USER = "ROA_WEB"
|
||||
_SECRETS_DIR = os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "..", "..", "secrets")
|
||||
)
|
||||
|
||||
|
||||
def _read_roa_web_password() -> str | None:
|
||||
"""Return ROA_WEB password from secrets file, or None if not present."""
|
||||
path = os.path.join(_SECRETS_DIR, "roa_web.oracle_pass")
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
with open(path) as f:
|
||||
return f.read().strip() or None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shared fixture — one connection per test module
|
||||
# ---------------------------------------------------------------------------
|
||||
@pytest.fixture(scope="module")
|
||||
def roa_web_connection():
|
||||
"""
|
||||
Yields a sync oracledb.Connection as ROA_WEB.
|
||||
Skips the whole module if the user does not yet exist on the server.
|
||||
"""
|
||||
password = _read_roa_web_password()
|
||||
if not password:
|
||||
pytest.skip(
|
||||
"ROA_WEB password file not found in backend/secrets/ — "
|
||||
"user not yet created (Phase B deferred, see grants-audit.md §3)"
|
||||
)
|
||||
|
||||
try:
|
||||
conn = oracledb.connect(
|
||||
user=_ROA_WEB_USER,
|
||||
password=password,
|
||||
host=_HOST,
|
||||
port=_PORT,
|
||||
service_name=_SERVICE_NAME,
|
||||
)
|
||||
except oracledb.DatabaseError as e:
|
||||
err = e.args[0]
|
||||
code = getattr(err, "code", 0)
|
||||
if code == 1017:
|
||||
pytest.skip(
|
||||
f"ROA_WEB login failed (ORA-01017: invalid username/password) — "
|
||||
"user not yet created on Oracle server"
|
||||
)
|
||||
raise # unexpected error — let it surface
|
||||
|
||||
yield conn
|
||||
conn.close()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Negative tests — direct DML/SELECT must be rejected
|
||||
# ---------------------------------------------------------------------------
|
||||
@pytest.mark.integration
|
||||
def test_insert_direct_fails(roa_web_connection):
|
||||
"""
|
||||
ROA_WEB has no INSERT privilege on NOM_LUCRARI.
|
||||
Expected: ORA-01031 (insufficient privileges) or ORA-00942 (no table grant).
|
||||
"""
|
||||
conn = roa_web_connection
|
||||
with conn.cursor() as cur:
|
||||
with pytest.raises(oracledb.DatabaseError) as exc_info:
|
||||
cur.execute(
|
||||
"INSERT INTO MARIUSM_AUTO.NOM_LUCRARI (nrord, id_mod) "
|
||||
"VALUES ('TEST_GRANT_PROBE', 1200)"
|
||||
)
|
||||
err = exc_info.value.args[0]
|
||||
assert err.code in (1031, 942), (
|
||||
f"Expected ORA-01031 or ORA-00942, got ORA-{err.code}: {err.message}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_select_direct_fails(roa_web_connection):
|
||||
"""
|
||||
ROA_WEB has no SELECT privilege on NOM_LUCRARI.
|
||||
Expected: ORA-00942 (table or view does not exist).
|
||||
"""
|
||||
conn = roa_web_connection
|
||||
with conn.cursor() as cur:
|
||||
with pytest.raises(oracledb.DatabaseError) as exc_info:
|
||||
cur.execute(
|
||||
"SELECT COUNT(*) FROM MARIUSM_AUTO.NOM_LUCRARI WHERE ROWNUM < 2"
|
||||
)
|
||||
cur.fetchone()
|
||||
err = exc_info.value.args[0]
|
||||
assert err.code == 942, (
|
||||
f"Expected ORA-00942, got ORA-{err.code}: {err.message}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Positive test — SP execution must succeed (EXECUTE grant)
|
||||
# ---------------------------------------------------------------------------
|
||||
@pytest.mark.integration
|
||||
def test_exec_sp_succeeds(roa_web_connection):
|
||||
"""
|
||||
ROA_WEB has EXECUTE on SP_CREEAZA_COMANDA_PROTOTIP.
|
||||
The SP call must not raise ORA-01031 (insufficient privileges).
|
||||
A FK violation (ORA-02291) is acceptable — it proves the SP was reached.
|
||||
Transaction is always rolled back — no test data left in prod.
|
||||
"""
|
||||
conn = roa_web_connection
|
||||
with conn.cursor() as cur:
|
||||
out_id_ordl = cur.var(oracledb.NUMBER)
|
||||
out_nrord = cur.var(oracledb.STRING)
|
||||
|
||||
try:
|
||||
cur.callproc(
|
||||
"MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP",
|
||||
[
|
||||
1, # p_tip (FK DEV_TIP_DEVIZ)
|
||||
1, # p_id_masiniclient (placeholder)
|
||||
"TEST GRANT PROBE", # p_solicitari
|
||||
1, # p_id_firma
|
||||
out_id_ordl,
|
||||
out_nrord,
|
||||
],
|
||||
)
|
||||
except oracledb.DatabaseError as e:
|
||||
conn.rollback()
|
||||
err = e.args[0]
|
||||
# ORA-02291: FK violation — SP ran, data was bad → EXECUTE grant works
|
||||
if err.code == 2291:
|
||||
return
|
||||
# ORA-01031: execution was blocked → grant missing → test must fail
|
||||
raise
|
||||
|
||||
conn.rollback() # ALWAYS rollback — never persist test data
|
||||
|
||||
id_ordl = out_id_ordl.getvalue()
|
||||
nrord = out_nrord.getvalue()
|
||||
assert id_ordl is not None, "SP must return a non-null p_id_ordl"
|
||||
assert nrord, "SP must return a non-empty p_nrord"
|
||||
Reference in New Issue
Block a user