first commit
This commit is contained in:
94
schema.sql
Normal file
94
schema.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
-- Extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- For additional crypto if needed
|
||||
|
||||
-- Schema: api (exposed to PostgREST)
|
||||
CREATE SCHEMA api;
|
||||
|
||||
-- Users Table
|
||||
CREATE TABLE api.users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
email TEXT UNIQUE NOT NULL CHECK (email ~* '^.+@.+\..+$'),
|
||||
verifier TEXT NOT NULL, -- SRP-6a verifier
|
||||
salt TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Secrets Table
|
||||
CREATE TABLE api.secrets (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
owner_id UUID NOT NULL REFERENCES api.users(id),
|
||||
encrypted_data BYTEA NOT NULL,
|
||||
iv TEXT NOT NULL,
|
||||
auth_tag TEXT NOT NULL,
|
||||
type TEXT DEFAULT 'manual',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Access Policies (Many-to-Many for sharing)
|
||||
CREATE TABLE api.access_policies (
|
||||
secret_id UUID NOT NULL REFERENCES api.secrets(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES api.users(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
PRIMARY KEY (secret_id, user_id)
|
||||
);
|
||||
|
||||
-- Roles & Permissions (PostgREST)
|
||||
-- We need a role for the anonymous web user and one for authenticated users.
|
||||
-- Note: 'web_anon' and 'todo_user' are placeholders. We should use:
|
||||
-- 'web_anon' (unauthenticated)
|
||||
-- 'authenticated' (logged in via JWT)
|
||||
|
||||
CREATE ROLE web_anon NOLOGIN;
|
||||
GRANT USAGE ON SCHEMA api TO web_anon;
|
||||
GRANT SELECT ON api.users TO web_anon; -- Needed for login (salt lookup)
|
||||
|
||||
CREATE ROLE authenticated NOLOGIN;
|
||||
GRANT USAGE ON SCHEMA api TO authenticated;
|
||||
GRANT ALL ON api.secrets TO authenticated;
|
||||
GRANT SELECT ON api.access_policies TO authenticated;
|
||||
GRANT SELECT ON api.users TO authenticated;
|
||||
|
||||
-- Row Level Security (RLS)
|
||||
ALTER TABLE api.secrets ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Helper function to get current user ID from JWT
|
||||
CREATE OR REPLACE FUNCTION api.uid() RETURNS UUID AS $$
|
||||
SELECT NULLIF(current_setting('request.jwt.claim.sub', true), '')::uuid;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
-- RLS Policy for Secrets
|
||||
CREATE POLICY secrets_owner_policy ON api.secrets
|
||||
FOR ALL
|
||||
TO authenticated
|
||||
USING (
|
||||
owner_id = api.uid()
|
||||
)
|
||||
WITH CHECK (
|
||||
owner_id = api.uid()
|
||||
);
|
||||
|
||||
CREATE POLICY secrets_shared_policy ON api.secrets
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM api.access_policies
|
||||
WHERE secret_id = api.secrets.id
|
||||
AND user_id = api.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Grant privileges to the authenticator role (the one PostgREST connects as)
|
||||
-- In the docker-compose, we used user:pass. We need to grant role usage to 'user'.
|
||||
-- Ideally, create a separate 'authenticator' role.
|
||||
CREATE ROLE authenticator NOINHERIT LOGIN PASSWORD 'mysecretpassword';
|
||||
GRANT web_anon TO authenticator;
|
||||
GRANT authenticated TO authenticator;
|
||||
|
||||
-- BUT since we are using 'user' from the postgres docker image as the connector,
|
||||
-- we'll just grant to 'user' or 'postgres'.
|
||||
-- Simpler for dev: Grant to the user specified in connection string.
|
||||
GRANT web_anon TO "user";
|
||||
GRANT authenticated TO "user";
|
||||
Reference in New Issue
Block a user