gendesign/data/sql/100_tradein_fdw_role.sql
lekss361 6507a5af64 fixup(tradein): address deep-review BLOCK findings on FDW PR
Critical:
- data/sql/100_tradein_fdw_role.sql — remove hardcoded password literal;
  role created LOGIN only; explicit REVOKE perimeter on all
  tables/sequences/functions in public; only v_tradein_cad_buildings grant
  remains. Password is now set by .forgejo/workflows/deploy.yml ALTER ROLE
  bootstrap step reading GENDESIGN_FDW_PASSWORD from backend/.env.runtime.
- tradein-mvp/backend/app/core/fdw.py — strict regex whitelist on password
  ([A-Za-z0-9_-]{32,256}) before inlining into DDL; ValueError on mismatch
  (no password in error message); pg_user_mappings query corrected for
  PUBLIC mapping (usename IS NULL); commit wrapped in try/rollback.

High:
- 060_postgres_fdw_extension.sql — connect_timeout='3' added to FOREIGN
  SERVER; ALTER SERVER ADD/SET fallback for re-apply path.
- geocoder.py — _cadastral_forward_sync / _cadastral_reverse_sync wrapped in
  asyncio.to_thread to avoid blocking event loop on FDW outage.

Tests:
- 3 new test_cadastral_reverse.py cases: SQL injection password rejected,
  short password rejected, valid hex password accepted with mapping creation.

Old password 40473b7c... remains in git history at 698ef4f (force-push
forbidden) — but the role was never created with it on prod (SQL not
deployed), so abandoned. New password 72df... lives only in VPS env files
and vault meta/00_credentials.md.
2026-05-24 11:50:51 +03:00

45 lines
1.8 KiB
PL/PgSQL

-- tradein_fdw_reader role + v_tradein_cad_buildings view
-- Used by tradein-postgres via postgres_fdw to read live cad_buildings.
-- Password is set by .forgejo/workflows/deploy.yml ALTER ROLE bootstrap step
-- reading GENDESIGN_FDW_PASSWORD from backend/.env.runtime — never embed password in SQL.
BEGIN;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'tradein_fdw_reader') THEN
CREATE ROLE tradein_fdw_reader LOGIN;
END IF;
END$$;
COMMENT ON ROLE tradein_fdw_reader IS
'FDW reader from tradein-postgres. Password set by .forgejo/workflows/deploy.yml from env GENDESIGN_FDW_PASSWORD post-migration step. Never embed password in SQL migrations.';
-- Defense-in-depth: explicit REVOKE perimeter (any existing PUBLIC grants ignored).
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM tradein_fdw_reader;
REVOKE ALL ON ALL SEQUENCES IN SCHEMA public FROM tradein_fdw_reader;
REVOKE ALL ON ALL FUNCTIONS IN SCHEMA public FROM tradein_fdw_reader;
GRANT CONNECT ON DATABASE gendesign TO tradein_fdw_reader;
GRANT USAGE ON SCHEMA public TO tradein_fdw_reader;
CREATE OR REPLACE VIEW v_tradein_cad_buildings AS
SELECT
cad_num,
NULLIF(trim(readable_address), '') AS readable_address,
NULLIF(floors, 0) AS floors,
CASE WHEN year_built BETWEEN 1800 AND 2100 THEN year_built END AS year_built,
round(COALESCE(build_record_area, area), 2)::numeric AS area_m2,
purpose,
ST_Y(ST_Centroid(geom))::double precision AS lat,
ST_X(ST_Centroid(geom))::double precision AS lon
FROM cad_buildings
WHERE cad_num LIKE '66:41:%'
AND geom IS NOT NULL
AND readable_address IS NOT NULL;
GRANT SELECT ON v_tradein_cad_buildings TO tradein_fdw_reader;
COMMENT ON VIEW v_tradein_cad_buildings IS
'FDW source for tradein-mvp (postgres_fdw). Flat 8-col EKB-only slice of cad_buildings. tradein.gendesign_cad_buildings maps here.';
COMMIT;