diff --git a/schema/schema.sql b/schema/schema.sql index 194ede8..04f0f85 100644 --- a/schema/schema.sql +++ b/schema/schema.sql @@ -524,3 +524,20 @@ CREATE TABLE IF NOT EXISTS virtual_devices ); CREATE UNIQUE INDEX IF NOT EXISTS virtual_devices_crop ON virtual_devices (crop_id); CREATE INDEX IF NOT EXISTS virtual_devices_owner ON virtual_devices (owner_id); + +-- Conexiones personales (clientes MCP y similares). Solo se guarda el SHA-256 del token; el prefijo visible permite +-- reconocerlo en la PWA. READ solo consulta; CONTROL además crea, edita y acciona. Revocar es borrar la fila. +CREATE TABLE IF NOT EXISTS access_tokens +( + id CHAR(24) PRIMARY KEY, + user_id CHAR(24) NOT NULL, + name TEXT NOT NULL, + token_hash TEXT NOT NULL, + prefix TEXT NOT NULL, + scope TEXT NOT NULL CHECK (scope IN ('READ', 'CONTROL')), + created_at TIMESTAMPTZ NOT NULL, + last_used_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ +); +CREATE UNIQUE INDEX IF NOT EXISTS access_tokens_hash_key ON access_tokens (token_hash); +CREATE INDEX IF NOT EXISTS access_tokens_user ON access_tokens (user_id); diff --git a/tests/validate.sh b/tests/validate.sh index 4c53ba7..4313094 100644 --- a/tests/validate.sh +++ b/tests/validate.sh @@ -43,7 +43,9 @@ done fail=0 check() { if [ "$2" = "$3" ]; then echo "OK $1"; else echo "FAIL $1 (esperado $3, obtenido $2)"; fail=1; fi; } -check "diez tablas" "$(app "SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'")" "10" +check "once tablas" "$(app "SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'")" "11" +check "el alcance de una conexión se restringe" \ + "$(app "INSERT INTO access_tokens (id,user_id,name,token_hash,prefix,scope,created_at) VALUES ('0000000000000000000000a1','u','x','h','spt_x','ADMIN',now())" 2>&1 >/dev/null | grep -qi 'check\|violates' && echo rechazado || echo aceptado)" "rechazado" check "readings es una hipertabla de TimescaleDB" \ "$(app "SELECT count(*) FROM timescaledb_information.hypertables WHERE hypertable_name='readings'")" "1" check "readings tiene política de retención" \