Skip to main content
Glama
test_rls_policies.sql13.7 kB
-- ==================================================================== -- RLS Policy Tests -- Purpose: Verify Row-Level Security policies enforce multi-tenant isolation -- ==================================================================== BEGIN; -- Load pgTAP extension CREATE EXTENSION IF NOT EXISTS pgtap; -- ==================================================================== -- Test Setup: Create Test Users and Organizations -- ==================================================================== -- Create test users in auth.users -- Note: In real Supabase, these would be created via auth.signup() -- For testing, we'll use service role to insert directly SET LOCAL ROLE postgres; -- Test User 1 (Org A Owner) INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, created_at, updated_at) VALUES ('11111111-1111-1111-1111-111111111111'::uuid, 'user1@example.com', 'hashed', NOW(), NOW(), NOW()) ON CONFLICT (id) DO NOTHING; -- Test User 2 (Org B Owner) INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, created_at, updated_at) VALUES ('22222222-2222-2222-2222-222222222222'::uuid, 'user2@example.com', 'hashed', NOW(), NOW(), NOW()) ON CONFLICT (id) DO NOTHING; -- Test User 3 (Org A Member) INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, created_at, updated_at) VALUES ('33333333-3333-3333-3333-333333333333'::uuid, 'user3@example.com', 'hashed', NOW(), NOW(), NOW()) ON CONFLICT (id) DO NOTHING; -- Create test organizations INSERT INTO organizations (id, name, created_at, updated_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'Organization A', NOW(), NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, 'Organization B', NOW(), NOW()) ON CONFLICT (id) DO NOTHING; -- Create organization memberships INSERT INTO organization_members (organization_id, user_id, role, created_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, '11111111-1111-1111-1111-111111111111'::uuid, 'owner', NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '22222222-2222-2222-2222-222222222222'::uuid, 'owner', NOW()), ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, '33333333-3333-3333-3333-333333333333'::uuid, 'member', NOW()) ON CONFLICT (organization_id, user_id) DO NOTHING; -- Create test credentials INSERT INTO hostaway_credentials (organization_id, account_id, encrypted_secret_key, credentials_valid, created_at, updated_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'ACC_A_001', 'encrypted_secret_a', true, NOW(), NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, 'ACC_B_001', 'encrypted_secret_b', true, NOW(), NOW()) ON CONFLICT (organization_id) DO NOTHING; -- Create test API keys INSERT INTO api_keys (organization_id, key_hash, is_active, created_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'hash_a_001', true, NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, 'hash_b_001', true, NOW()) ON CONFLICT DO NOTHING; -- Create test subscriptions INSERT INTO subscriptions (organization_id, stripe_subscription_id, status, current_quantity, created_at, updated_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'sub_a_001', 'active', 5, NOW(), NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, 'sub_b_001', 'active', 3, NOW(), NOW()) ON CONFLICT (organization_id) DO NOTHING; -- Create test usage metrics INSERT INTO usage_metrics (organization_id, month_year, total_api_requests, unique_tools_used, created_at, updated_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, '2025-10', 100, ARRAY['properties', 'listings'], NOW(), NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '2025-10', 50, ARRAY['reservations'], NOW(), NOW()) ON CONFLICT (organization_id, month_year) DO NOTHING; -- Create test audit logs INSERT INTO audit_logs (organization_id, action, details, created_at) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'credential_updated', '{"account_id": "ACC_A_001"}', NOW()), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, 'api_key_created', '{"key_id": "key_b_001"}', NOW()); -- ==================================================================== -- Test Plan -- ==================================================================== SELECT plan(28); -- ==================================================================== -- 1. organizations Table RLS Tests -- ==================================================================== -- Test: User 1 can see their own organization (Org A) SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT id::text FROM organizations', ARRAY['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'], 'User 1 can only see Organization A' ); -- Test: User 2 can see their own organization (Org B) SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT results_eq( 'SELECT id::text FROM organizations', ARRAY['bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'], 'User 2 can only see Organization B' ); -- Test: User 1 cannot see Organization B SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM organizations WHERE id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Organization B' ); -- ==================================================================== -- 2. organization_members Table RLS Tests -- ==================================================================== -- Test: User 1 can see their own membership SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT user_id::text FROM organization_members', ARRAY['11111111-1111-1111-1111-111111111111'], 'User 1 can see their own membership' ); -- Test: User 2 can see their own membership SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT results_eq( 'SELECT user_id::text FROM organization_members', ARRAY['22222222-2222-2222-2222-222222222222'], 'User 2 can see their own membership' ); -- Test: User 1 (owner) can see all Org A members SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT user_id::text FROM organization_members WHERE organization_id = ''aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa''::uuid ORDER BY user_id', ARRAY['11111111-1111-1111-1111-111111111111', '33333333-3333-3333-3333-333333333333'], 'Owner can see all org members' ); -- Test: User 3 (member) cannot see other members SET LOCAL "request.jwt.claims" TO '{"sub": "33333333-3333-3333-3333-333333333333"}'; SELECT results_eq( 'SELECT user_id::text FROM organization_members', ARRAY['33333333-3333-3333-3333-333333333333'], 'Member can only see their own membership' ); -- Test: User 1 cannot see User 2's membership SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM organization_members WHERE user_id = '22222222-2222-2222-2222-222222222222'::uuid), 0::bigint, 'User 1 cannot see User 2 membership' ); -- ==================================================================== -- 3. hostaway_credentials Table RLS Tests -- ==================================================================== -- Test: User 1 can see Org A credentials SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT organization_id::text FROM hostaway_credentials', ARRAY['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'], 'User 1 can see Org A credentials' ); -- Test: User 2 can see Org B credentials SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT results_eq( 'SELECT organization_id::text FROM hostaway_credentials', ARRAY['bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'], 'User 2 can see Org B credentials' ); -- Test: User 1 cannot see Org B credentials SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM hostaway_credentials WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Org B credentials' ); -- Test: User 3 (member) can see Org A credentials SET LOCAL "request.jwt.claims" TO '{"sub": "33333333-3333-3333-3333-333333333333"}'; SELECT results_eq( 'SELECT organization_id::text FROM hostaway_credentials', ARRAY['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'], 'Member can see org credentials' ); -- ==================================================================== -- 4. api_keys Table RLS Tests -- ==================================================================== -- Test: User 1 can see Org A API keys SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM api_keys WHERE organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid), 1::bigint, 'User 1 can see Org A API keys' ); -- Test: User 2 can see Org B API keys SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT is( (SELECT COUNT(*) FROM api_keys WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 1::bigint, 'User 2 can see Org B API keys' ); -- Test: User 1 cannot see Org B API keys SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM api_keys WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Org B API keys' ); -- Test: User 3 (member) can see Org A API keys SET LOCAL "request.jwt.claims" TO '{"sub": "33333333-3333-3333-3333-333333333333"}'; SELECT is( (SELECT COUNT(*) FROM api_keys WHERE organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid), 1::bigint, 'Member can see org API keys' ); -- ==================================================================== -- 5. subscriptions Table RLS Tests -- ==================================================================== -- Test: User 1 can see Org A subscription SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT organization_id::text FROM subscriptions', ARRAY['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'], 'User 1 can see Org A subscription' ); -- Test: User 2 can see Org B subscription SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT results_eq( 'SELECT organization_id::text FROM subscriptions', ARRAY['bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'], 'User 2 can see Org B subscription' ); -- Test: User 1 cannot see Org B subscription SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM subscriptions WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Org B subscription' ); -- ==================================================================== -- 6. usage_metrics Table RLS Tests -- ==================================================================== -- Test: User 1 can see Org A usage metrics SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT results_eq( 'SELECT organization_id::text FROM usage_metrics', ARRAY['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'], 'User 1 can see Org A usage metrics' ); -- Test: User 2 can see Org B usage metrics SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT results_eq( 'SELECT organization_id::text FROM usage_metrics', ARRAY['bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'], 'User 2 can see Org B usage metrics' ); -- Test: User 1 cannot see Org B usage metrics SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM usage_metrics WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Org B usage metrics' ); -- ==================================================================== -- 7. audit_logs Table RLS Tests -- ==================================================================== -- Test: User 1 can see Org A audit logs SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM audit_logs WHERE organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid), 1::bigint, 'User 1 can see Org A audit logs' ); -- Test: User 2 can see Org B audit logs SET LOCAL "request.jwt.claims" TO '{"sub": "22222222-2222-2222-2222-222222222222"}'; SELECT is( (SELECT COUNT(*) FROM audit_logs WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 1::bigint, 'User 2 can see Org B audit logs' ); -- Test: User 1 cannot see Org B audit logs SET LOCAL "request.jwt.claims" TO '{"sub": "11111111-1111-1111-1111-111111111111"}'; SELECT is( (SELECT COUNT(*) FROM audit_logs WHERE organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid), 0::bigint, 'User 1 cannot see Org B audit logs' ); -- Test: User 3 (member) can see Org A audit logs SET LOCAL "request.jwt.claims" TO '{"sub": "33333333-3333-3333-3333-333333333333"}'; SELECT is( (SELECT COUNT(*) FROM audit_logs WHERE organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid), 1::bigint, 'Member can see org audit logs' ); -- ==================================================================== -- Test Completion -- ==================================================================== SELECT * FROM finish(); ROLLBACK;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/darrentmorgan/hostaway-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server