Terminal
This commit is contained in:
@@ -1,63 +0,0 @@
|
|||||||
-- Embedded mail: one shared IMAP/SMTP server configured by admins, with a
|
|
||||||
-- per-user mailbox login.
|
|
||||||
--
|
|
||||||
-- Threat model drives the shape here. A mailbox password grants full read and
|
|
||||||
-- send access to someone's email, so:
|
|
||||||
--
|
|
||||||
-- * user_mailboxes has RLS enabled and NO policies at all. Nothing reachable
|
|
||||||
-- from a browser can read it — not even the mailbox owner, and not admins.
|
|
||||||
-- Every access goes through server functions using the service role, the
|
|
||||||
-- same pattern intake_tokens uses.
|
|
||||||
-- * The password is never stored as plaintext. It is sealed with AES-256-GCM
|
|
||||||
-- using MAIL_CRED_KEY from .env.secret, so a database dump alone is not
|
|
||||||
-- enough to open anyone's mail.
|
|
||||||
-- * The IMAP/SMTP *host* settings are not secret, so admins may read those
|
|
||||||
-- directly to populate the setup form.
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.mail_server_settings (
|
|
||||||
-- Single-row table: the school has one mail server.
|
|
||||||
id BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (id = TRUE),
|
|
||||||
imap_host TEXT NOT NULL,
|
|
||||||
imap_port INT NOT NULL DEFAULT 993,
|
|
||||||
imap_secure BOOLEAN NOT NULL DEFAULT TRUE, -- implicit TLS on connect
|
|
||||||
smtp_host TEXT NOT NULL,
|
|
||||||
smtp_port INT NOT NULL DEFAULT 587,
|
|
||||||
smtp_secure BOOLEAN NOT NULL DEFAULT FALSE, -- false = STARTTLS upgrade
|
|
||||||
updated_by UUID REFERENCES auth.users(id),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
||||||
);
|
|
||||||
GRANT SELECT ON public.mail_server_settings TO authenticated;
|
|
||||||
GRANT ALL ON public.mail_server_settings TO service_role;
|
|
||||||
ALTER TABLE public.mail_server_settings ENABLE ROW LEVEL SECURITY;
|
|
||||||
|
|
||||||
-- Host/port are operational config, not secrets. Admins read them to fill in
|
|
||||||
-- the setup form; writes go through a server function so they can be validated
|
|
||||||
-- against a live login first.
|
|
||||||
CREATE POLICY "mail settings admin read" ON public.mail_server_settings FOR SELECT TO authenticated
|
|
||||||
USING (public.current_user_has_role('admin'));
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.user_mailboxes (
|
|
||||||
user_id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
||||||
email TEXT NOT NULL,
|
|
||||||
-- AES-256-GCM sealed mailbox password. Never returned to any client.
|
|
||||||
secret_ciphertext TEXT NOT NULL,
|
|
||||||
secret_iv TEXT NOT NULL,
|
|
||||||
secret_tag TEXT NOT NULL,
|
|
||||||
last_verified_at TIMESTAMPTZ,
|
|
||||||
created_by UUID REFERENCES auth.users(id),
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_user_mailboxes_email ON public.user_mailboxes(email);
|
|
||||||
GRANT ALL ON public.user_mailboxes TO service_role;
|
|
||||||
ALTER TABLE public.user_mailboxes ENABLE ROW LEVEL SECURITY;
|
|
||||||
-- Deliberately no policies and no grant to `authenticated`: credentials must
|
|
||||||
-- never be reachable from the browser under any role.
|
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS trg_mail_settings_upd ON public.mail_server_settings;
|
|
||||||
CREATE TRIGGER trg_mail_settings_upd BEFORE UPDATE ON public.mail_server_settings
|
|
||||||
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS trg_user_mailboxes_upd ON public.user_mailboxes;
|
|
||||||
CREATE TRIGGER trg_user_mailboxes_upd BEFORE UPDATE ON public.user_mailboxes
|
|
||||||
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
|
||||||
Reference in New Issue
Block a user