Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,42 @@ COMMENT ON CONSTRAINT tokens_table_fkey ON metaschema_modules_public.rls_module
COMMENT ON CONSTRAINT users_table_fkey ON metaschema_modules_public.rls_module IS E'@omit';
CREATE INDEX rls_module_database_id_idx ON metaschema_modules_public.rls_module ( database_id );

-- database_settings table (typed feature flags — database-wide defaults)
CREATE TABLE IF NOT EXISTS services_public.database_settings (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
database_id uuid NOT NULL UNIQUE,
enable_aggregates boolean NOT NULL DEFAULT false,
enable_postgis boolean NOT NULL DEFAULT true,
enable_search boolean NOT NULL DEFAULT true,
enable_direct_uploads boolean NOT NULL DEFAULT true,
enable_presigned_uploads boolean NOT NULL DEFAULT true,
enable_many_to_many boolean NOT NULL DEFAULT true,
enable_connection_filter boolean NOT NULL DEFAULT true,
enable_ltree boolean NOT NULL DEFAULT true,
enable_llm boolean NOT NULL DEFAULT false,
options jsonb NOT NULL DEFAULT '{}'::jsonb,
CONSTRAINT ds_db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE
);

-- api_settings table (per-API overrides — NULL = inherit from database_settings)
CREATE TABLE IF NOT EXISTS services_public.api_settings (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
database_id uuid NOT NULL,
api_id uuid NOT NULL UNIQUE,
enable_aggregates boolean,
enable_postgis boolean,
enable_search boolean,
enable_direct_uploads boolean,
enable_presigned_uploads boolean,
enable_many_to_many boolean,
enable_connection_filter boolean,
enable_ltree boolean,
enable_llm boolean,
options jsonb NOT NULL DEFAULT '{}'::jsonb,
CONSTRAINT as_db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE,
CONSTRAINT as_api_fkey FOREIGN KEY (api_id) REFERENCES services_public.apis (id) ON DELETE CASCADE
);

-- Grant permissions on metaschema tables
GRANT SELECT, INSERT, UPDATE, DELETE ON metaschema_public.database TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON metaschema_public.schema TO administrator, authenticated, anonymous;
Expand All @@ -301,4 +337,6 @@ GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.domains TO administrator
GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.api_schemas TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.api_extensions TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.api_modules TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.database_settings TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON services_public.api_settings TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON metaschema_modules_public.rls_module TO administrator, authenticated, anonymous;
205 changes: 147 additions & 58 deletions graphql/server-test/__fixtures__/seed/simple-seed-storage/schema.sql
Original file line number Diff line number Diff line change
@@ -1,59 +1,148 @@
-- Schema creation for simple-seed-storage test scenario
-- Creates the app schema with storage tables (buckets, files)

-- Create app schemas
CREATE SCHEMA IF NOT EXISTS "simple-storage-public";

-- Grant schema usage
GRANT USAGE ON SCHEMA "simple-storage-public" TO administrator, authenticated, anonymous;

-- Set default privileges
ALTER DEFAULT PRIVILEGES IN SCHEMA "simple-storage-public"
GRANT ALL ON TABLES TO administrator;
ALTER DEFAULT PRIVILEGES IN SCHEMA "simple-storage-public"
GRANT USAGE ON SEQUENCES TO administrator, authenticated;
ALTER DEFAULT PRIVILEGES IN SCHEMA "simple-storage-public"
GRANT ALL ON FUNCTIONS TO administrator, authenticated, anonymous;

-- =====================================================
-- STORAGE TABLES (mirroring what the storage module generator creates)
-- =====================================================

-- Buckets table
CREATE TABLE IF NOT EXISTS "simple-storage-public".app_buckets (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
key text NOT NULL,
type text NOT NULL DEFAULT 'private',
is_public boolean NOT NULL DEFAULT false,
allowed_mime_types text[] NULL,
max_file_size bigint NULL,
allow_custom_keys boolean NOT NULL DEFAULT false,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
UNIQUE (key)
);

COMMENT ON TABLE "simple-storage-public".app_buckets IS E'@storageBuckets\nStorage buckets table';

-- Files table
CREATE TABLE IF NOT EXISTS "simple-storage-public".app_files (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
bucket_id uuid NOT NULL REFERENCES "simple-storage-public".app_buckets(id),
key text NOT NULL,
content_hash text NOT NULL,
mime_type text NOT NULL,
size bigint,
filename text,
owner_id uuid,
is_public boolean NOT NULL DEFAULT false,
previous_version_id uuid REFERENCES "simple-storage-public".app_files(id),
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
UNIQUE (bucket_id, key)
);

COMMENT ON TABLE "simple-storage-public".app_files IS E'@storageFiles\nStorage files table';

-- Grant table permissions (allow anonymous to do CRUD for tests — no RLS)
GRANT SELECT, INSERT, UPDATE, DELETE ON "simple-storage-public".app_buckets TO administrator, authenticated, anonymous;
GRANT SELECT, INSERT, UPDATE, DELETE ON "simple-storage-public".app_files TO administrator, authenticated, anonymous;
-- Creates storage schemas (buckets + files) for three tenants:
-- Alice (no RLS), Bob (moderate RLS), Mallory (strictest RLS)

-- =====================================================
-- Helper: create a storage schema with buckets + files tables
-- =====================================================

CREATE FUNCTION _test_create_storage_schema(schema_name text) RETURNS void
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I', schema_name);

EXECUTE format('GRANT USAGE ON SCHEMA %I TO administrator, authenticated, anonymous', schema_name);

EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO administrator', schema_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT USAGE ON SEQUENCES TO administrator, authenticated', schema_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON FUNCTIONS TO administrator, authenticated, anonymous', schema_name);

-- Buckets table
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I.app_buckets (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
key text NOT NULL,
type text NOT NULL DEFAULT ''private'',
is_public boolean NOT NULL DEFAULT false,
allowed_mime_types text[] NULL,
max_file_size bigint NULL,
allow_custom_keys boolean NOT NULL DEFAULT false,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
UNIQUE (key)
)', schema_name);

EXECUTE format(
'COMMENT ON TABLE %I.app_buckets IS E''@storageBuckets\nStorage buckets table''',
schema_name);

-- Files table
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I.app_files (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
bucket_id uuid NOT NULL REFERENCES %I.app_buckets(id),
key text NOT NULL,
content_hash text NOT NULL,
mime_type text NOT NULL,
size bigint,
filename text,
owner_id uuid,
is_public boolean NOT NULL DEFAULT false,
previous_version_id uuid REFERENCES %I.app_files(id),
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
UNIQUE (bucket_id, key)
)', schema_name, schema_name, schema_name);

EXECUTE format(
'COMMENT ON TABLE %I.app_files IS E''@storageFiles\nStorage files table''',
schema_name);

-- Grant CRUD to all roles
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON %I.app_buckets TO administrator, authenticated, anonymous', schema_name);
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON %I.app_files TO administrator, authenticated, anonymous', schema_name);
END;
$$;

-- =====================================================
-- ALICE (no RLS — wide open)
-- =====================================================

SELECT _test_create_storage_schema('simple-storage-public');

-- =====================================================
-- BOB (moderate RLS)
-- Buckets: anonymous sees public only
-- Files: anonymous can SELECT public-bucket files + INSERT; no UPDATE/DELETE
-- =====================================================

SELECT _test_create_storage_schema('bob-storage-public');

ALTER TABLE "bob-storage-public".app_buckets ENABLE ROW LEVEL SECURITY;

CREATE POLICY anon_read_public_buckets ON "bob-storage-public".app_buckets
FOR SELECT TO anonymous
USING (is_public = true);

CREATE POLICY admin_all_buckets ON "bob-storage-public".app_buckets
FOR ALL TO administrator
USING (true)
WITH CHECK (true);

ALTER TABLE "bob-storage-public".app_files ENABLE ROW LEVEL SECURITY;

CREATE POLICY anon_read_public_files ON "bob-storage-public".app_files
FOR SELECT TO anonymous
USING (
bucket_id IN (
SELECT id FROM "bob-storage-public".app_buckets WHERE is_public = true
)
);

-- Anonymous can insert into any bucket (for upload testing)
CREATE POLICY anon_insert_files ON "bob-storage-public".app_files
FOR INSERT TO anonymous
WITH CHECK (true);

-- No UPDATE or DELETE policies for anonymous — absence means denied.
-- This prevents Supabase-style attacks where anonymous could:
-- - change a file's bucket_id from private to public
-- - flip is_public flags
-- - delete other users' files

CREATE POLICY admin_all_files ON "bob-storage-public".app_files
FOR ALL TO administrator
USING (true)
WITH CHECK (true);

-- =====================================================
-- MALLORY (strictest RLS — anonymous can only SELECT)
-- =====================================================

SELECT _test_create_storage_schema('mallory-storage-public');

ALTER TABLE "mallory-storage-public".app_buckets ENABLE ROW LEVEL SECURITY;

CREATE POLICY anon_read_buckets ON "mallory-storage-public".app_buckets
FOR SELECT TO anonymous
USING (true);

CREATE POLICY admin_all_buckets ON "mallory-storage-public".app_buckets
FOR ALL TO administrator
USING (true)
WITH CHECK (true);

ALTER TABLE "mallory-storage-public".app_files ENABLE ROW LEVEL SECURITY;

-- Anonymous can only read (no INSERT/UPDATE/DELETE at all)
CREATE POLICY anon_read_files ON "mallory-storage-public".app_files
FOR SELECT TO anonymous
USING (true);

CREATE POLICY admin_all_files ON "mallory-storage-public".app_files
FOR ALL TO administrator
USING (true)
WITH CHECK (true);

-- Clean up helper
DROP FUNCTION _test_create_storage_schema(text);
Loading
Loading