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.
45 lines
1.8 KiB
PL/PgSQL
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;
|