-- Hammy backend bootstrap. Run ONCE, as a superuser, before any migration:
--
--     psql -U postgres -f scripts/bootstrap.sql
--
-- Deliberately NOT in migrations/. Three reasons:
--
--   * CREATE DATABASE cannot run inside a transaction block, and migration
--     tools wrap each file in one.
--   * \c is a psql meta-command, not SQL. A migration tool has no idea what it
--     means.
--   * Roles are cluster-wide, not per-database, so they are not part of any one
--     database's schema history.
--
-- Order matters: the schemas and ALTER DEFAULT PRIVILEGES must exist BEFORE any
-- table is created, because default privileges only apply to objects created
-- afterwards. Get this right here and no migration ever needs a GRANT.

-- ---------------------------------------------------------------------------
-- Roles (cluster-wide)
-- ---------------------------------------------------------------------------

-- Owns every object and runs migrations. Never connects at runtime.
CREATE ROLE hammy_owner NOLOGIN;

-- What the backend connects as. DML only: it can read and write rows but not
-- create, alter or drop anything, so a compromised backend cannot drop the
-- logbook.
CREATE ROLE hammy_app LOGIN PASSWORD 'CHANGE_ME';

-- Bulk loaders for FCC ULS, 44net allocations and the like.
CREATE ROLE hammy_ingest LOGIN PASSWORD 'CHANGE_ME';

-- Analytics and debugging. Deliberately no access to the api schema - a
-- reporting query has no business reading key material.
CREATE ROLE hammy_readonly LOGIN PASSWORD 'CHANGE_ME';

-- ---------------------------------------------------------------------------
-- Database
-- ---------------------------------------------------------------------------

CREATE DATABASE hammy OWNER hammy_owner ENCODING 'UTF8';

\c hammy

-- ---------------------------------------------------------------------------
-- Schemas
-- ---------------------------------------------------------------------------

-- Before PostgreSQL 15, PUBLIC held CREATE on the public schema, so any role
-- could add objects to it. Revoke it and do not use public for anything.
REVOKE ALL ON SCHEMA public FROM PUBLIC;

CREATE SCHEMA core     AUTHORIZATION hammy_owner;
CREATE SCHEMA api      AUTHORIZATION hammy_owner;
CREATE SCHEMA logbook  AUTHORIZATION hammy_owner;
CREATE SCHEMA ingest   AUTHORIZATION hammy_owner;

COMMENT ON SCHEMA core    IS 'Discord-linked user identity and callsign verification.';
COMMENT ON SCHEMA api     IS 'API tenancy: owners, keys, usage. Most sensitive schema.';
COMMENT ON SCHEMA logbook IS 'Per-user QSO logs and import batches.';
COMMENT ON SCHEMA ingest  IS 'Bulk-loaded third-party reference data.';

GRANT USAGE ON SCHEMA core, api, logbook, ingest TO hammy_app;
GRANT USAGE ON SCHEMA ingest TO hammy_ingest;
GRANT USAGE ON SCHEMA core, logbook TO hammy_readonly;

-- ---------------------------------------------------------------------------
-- Default privileges
--
-- Apply to objects hammy_owner creates FROM NOW ON. Every migration runs as
-- hammy_owner, so tables pick these up automatically.
-- ---------------------------------------------------------------------------

ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA core, api, logbook
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO hammy_app;

ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA core, api, logbook
    GRANT USAGE ON SEQUENCES TO hammy_app;

ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA ingest
    GRANT SELECT ON TABLES TO hammy_app;

ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA ingest
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO hammy_ingest;

ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA core, logbook
    GRANT SELECT ON TABLES TO hammy_readonly;

-- Postgres grants EXECUTE on new functions to PUBLIC by default, which would
-- make forget_user() callable by anyone who can connect. Revoke it; 005 grants
-- it back to hammy_app explicitly.
ALTER DEFAULT PRIVILEGES FOR ROLE hammy_owner IN SCHEMA core, api
    REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

-- ---------------------------------------------------------------------------
-- search_path per role, so queries need not schema-qualify everything.
-- ---------------------------------------------------------------------------

ALTER ROLE hammy_owner    SET search_path = core, api, logbook, ingest;
ALTER ROLE hammy_app      SET search_path = core, api, logbook, ingest;
ALTER ROLE hammy_ingest   SET search_path = ingest;
ALTER ROLE hammy_readonly SET search_path = core, logbook;
