-- 101_gendesign_reader_role.sql -- Reader-роль для cross-load ETL gendesign → tradein (#976 950-E5). -- Зеркало паттерна gendesign/data/sql/100_tradein_fdw_role.sql. -- -- Роль gendesign_reader создаётся без пароля — пароль устанавливается -- deploy-tradein.yml из env TRADEIN_READER_PASSWORD (bootstrap-шаг после миграций). -- Никогда не embed password в SQL. -- -- Доступ: ТОЛЬКО SELECT на houses, house_sources, houses_price_dynamics, -- house_reliability_checks, house_reviews (источники cross-load ETL #976). -- -- Идемпотентно: DO $$ IF NOT EXISTS — безопасно при повторном применении. -- AUTO-APPLIED на прод при деплое через deploy-tradein.yml/_schema_migrations. BEGIN; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'gendesign_reader') THEN CREATE ROLE gendesign_reader LOGIN; END IF; END$$; COMMENT ON ROLE gendesign_reader IS 'Cross-load ETL reader from gendesign-backend (#976). ' 'Password set by .forgejo/workflows/deploy-tradein.yml from env TRADEIN_READER_PASSWORD ' 'post-migration step. Never embed password in SQL migrations.'; -- Defense-in-depth: явный REVOKE-периметр (любые PUBLIC-гранты игнорируются). REVOKE ALL ON ALL TABLES IN SCHEMA public FROM gendesign_reader; REVOKE ALL ON ALL SEQUENCES IN SCHEMA public FROM gendesign_reader; REVOKE ALL ON ALL FUNCTIONS IN SCHEMA public FROM gendesign_reader; GRANT CONNECT ON DATABASE tradein TO gendesign_reader; GRANT USAGE ON SCHEMA public TO gendesign_reader; -- SELECT только на таблицах-источниках ETL #976 (не весь public). GRANT SELECT ON houses TO gendesign_reader; GRANT SELECT ON house_sources TO gendesign_reader; GRANT SELECT ON houses_price_dynamics TO gendesign_reader; GRANT SELECT ON house_reliability_checks TO gendesign_reader; GRANT SELECT ON house_reviews TO gendesign_reader; COMMIT;