Skip to content

Commit fee8df0

Browse files
committed
simplify
1 parent 99529c3 commit fee8df0

File tree

3 files changed

+142
-57
lines changed

3 files changed

+142
-57
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { getConnections, PgTestClient } from 'supabase-test';
2+
import { insertUser } from '../test-utils';
3+
4+
let db: PgTestClient;
5+
let pg: PgTestClient;
6+
let teardown: () => Promise<void>;
7+
8+
let user1: any;
9+
let user2: any;
10+
11+
beforeAll(async () => {
12+
({ pg, db, teardown } = await getConnections());
13+
user1 = await insertUser(pg, '[email protected]');
14+
user2 = await insertUser(pg, '[email protected]');
15+
});
16+
17+
afterAll(async () => {
18+
await teardown();
19+
});
20+
21+
beforeEach(async () => {
22+
await db.beforeEach();
23+
});
24+
25+
afterEach(async () => {
26+
await db.afterEach();
27+
});
28+
29+
describe('tutorial: basic rls crud operations', () => {
30+
it('should allow user to create their own user record', async () => {
31+
// set context to simulate authenticated user
32+
db.setContext({
33+
role: 'authenticated',
34+
'request.jwt.claim.sub': user1.id
35+
});
36+
37+
// user can create their own pet
38+
const pet = await db.one(
39+
`INSERT INTO rls_test.pets (name, breed, user_id)
40+
VALUES ($1, $2, $3)
41+
RETURNING id, name, breed, user_id`,
42+
['Fido', 'Labrador', user1.id]
43+
);
44+
45+
expect(pet.name).toBe('Fido');
46+
expect(pet.breed).toBe('Labrador');
47+
expect(pet.user_id).toBe(user1.id);
48+
});
49+
50+
it('should prevent user1 from updating user2\'s record', async () => {
51+
// user2 creates a pet
52+
db.setContext({
53+
role: 'authenticated',
54+
'request.jwt.claim.sub': user2.id
55+
});
56+
57+
const pet = await db.one(
58+
`INSERT INTO rls_test.pets (name, breed, user_id)
59+
VALUES ($1, $2, $3)
60+
RETURNING id, name, breed, user_id`,
61+
['Buddy', 'Golden Retriever', user2.id]
62+
);
63+
64+
expect(pet.user_id).toBe(user2.id);
65+
66+
// user1 tries to update user2's pet - should throw
67+
db.setContext({
68+
role: 'authenticated',
69+
'request.jwt.claim.sub': user1.id
70+
});
71+
72+
await expect(
73+
db.one(
74+
`UPDATE rls_test.pets
75+
SET name = $1
76+
WHERE id = $2
77+
RETURNING id, name, breed, user_id`,
78+
['Hacked Name', pet.id]
79+
)
80+
).rejects.toThrow();
81+
});
82+
});
83+
Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,59 @@
11
-- Deploy: rls-demo to pg
22
-- made with <3 @ launchql.com
33

4-
54
-- Create rls_test schema
65
CREATE SCHEMA IF NOT EXISTS rls_test;
76

87
-- Create users table
9-
CREATE TABLE IF NOT EXISTS rls_test.user_profiles (
8+
CREATE TABLE IF NOT EXISTS rls_test.pets (
109
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
11-
email TEXT UNIQUE NOT NULL,
10+
11+
-- owner_id is the user_id of the user who owns the pet
12+
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
13+
14+
-- name is the name of the pet
1215
name TEXT NOT NULL,
13-
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
14-
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
15-
);
16+
-- breed is the breed of the pet
17+
breed TEXT NOT NULL,
1618

17-
-- Create products table with owner_id foreign key
18-
CREATE TABLE IF NOT EXISTS rls_test.products (
19-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
20-
name TEXT NOT NULL,
21-
description TEXT,
22-
price DECIMAL(10,2) NOT NULL,
23-
owner_id UUID NOT NULL REFERENCES rls_test.user_profiles(id) ON DELETE CASCADE,
2419
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
2520
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
2621
);
2722

2823
-- Enable RLS on both tables
29-
ALTER TABLE rls_test.user_profiles ENABLE ROW LEVEL SECURITY;
30-
ALTER TABLE rls_test.products ENABLE ROW LEVEL SECURITY;
24+
ALTER TABLE rls_test.pets ENABLE ROW LEVEL SECURITY;
3125

3226
-- Create RLS policies for users table
3327
-- Users can view their own data
34-
CREATE POLICY "Users can view own data" ON rls_test.user_profiles
35-
FOR SELECT USING (auth.uid() = id);
28+
CREATE POLICY "Users can view own data" ON rls_test.pets
29+
FOR SELECT USING (auth.uid() = user_id);
3630

3731
-- Users can update their own data
38-
CREATE POLICY "Users can update own data" ON rls_test.user_profiles
39-
FOR UPDATE USING (auth.uid() = id);
32+
CREATE POLICY "Users can update own data" ON rls_test.pets
33+
FOR UPDATE USING (auth.uid() = user_id);
4034

4135
-- Users can insert their own data
42-
CREATE POLICY "Users can insert own data" ON rls_test.user_profiles
43-
FOR INSERT WITH CHECK (true);
36+
CREATE POLICY "Users can insert own data" ON rls_test.pets
37+
FOR INSERT WITH CHECK (auth.uid() = user_id);
4438

4539
-- Users can delete their own data
46-
CREATE POLICY "Users can delete own data" ON rls_test.user_profiles
47-
FOR DELETE USING (auth.uid() = id);
48-
49-
-- Create RLS policies for products table
50-
-- Users can view products they own
51-
CREATE POLICY "Users can view own products" ON rls_test.products
52-
FOR SELECT USING (auth.uid() = owner_id);
53-
54-
-- Users can insert products they own
55-
CREATE POLICY "Users can insert own products" ON rls_test.products
56-
FOR INSERT WITH CHECK (auth.uid() = owner_id);
57-
58-
-- Users can update products they own
59-
CREATE POLICY "Users can update own products" ON rls_test.products
60-
FOR UPDATE USING (auth.uid() = owner_id);
61-
62-
-- Users can delete products they own
63-
CREATE POLICY "Users can delete own products" ON rls_test.products
64-
FOR DELETE USING (auth.uid() = owner_id);
40+
CREATE POLICY "Users can delete own data" ON rls_test.pets
41+
FOR DELETE USING (auth.uid() = user_id);
6542

6643
-- Grant permissions to anon users
6744
GRANT USAGE ON SCHEMA rls_test TO anon;
68-
GRANT ALL ON rls_test.user_profiles TO anon;
69-
70-
-- dev (TODO: issue theres a chance we might want all anon to be not granted, but were protecting records)
71-
GRANT ALL ON rls_test.products TO anon;
45+
GRANT ALL ON rls_test.pets TO anon;
7246

7347
-- Grant permissions to authenticated users
7448
GRANT USAGE ON SCHEMA rls_test TO authenticated;
75-
GRANT ALL ON rls_test.user_profiles TO authenticated;
76-
GRANT ALL ON rls_test.products TO authenticated;
49+
GRANT ALL ON rls_test.pets TO authenticated;
7750

7851
-- Grant permissions to service role (for admin operations)
7952
GRANT USAGE ON SCHEMA rls_test TO service_role;
80-
GRANT ALL ON rls_test.user_profiles TO service_role;
81-
GRANT ALL ON rls_test.products TO service_role;
53+
GRANT ALL ON rls_test.pets TO service_role;
8254

8355
-- Create indexes for better performance
84-
CREATE INDEX IF NOT EXISTS idx_products_owner_id ON rls_test.products(owner_id);
85-
CREATE INDEX IF NOT EXISTS idx_users_email ON rls_test.user_profiles(email);
56+
CREATE INDEX IF NOT EXISTS idx_users_user_id ON rls_test.pets(user_id);
8657

8758
-- Create updated_at trigger function
8859
CREATE OR REPLACE FUNCTION rls_test.update_updated_at_column()
@@ -95,11 +66,6 @@ $$ LANGUAGE plpgsql;
9566

9667
-- Create triggers for updated_at
9768
CREATE TRIGGER update_users_updated_at
98-
BEFORE UPDATE ON rls_test.user_profiles
99-
FOR EACH ROW
100-
EXECUTE FUNCTION rls_test.update_updated_at_column();
101-
102-
CREATE TRIGGER update_products_updated_at
103-
BEFORE UPDATE ON rls_test.products
69+
BEFORE UPDATE ON rls_test.pets
10470
FOR EACH ROW
105-
EXECUTE FUNCTION rls_test.update_updated_at_column();
71+
EXECUTE FUNCTION rls_test.update_updated_at_column();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { PgTestClient } from "supabase-test";
2+
3+
/**
4+
* Helper function to insert a new user into auth.users
5+
* @param client - The PgTestClient to use (pg or db)
6+
* @param email - The user's email address
7+
* @param options - Optional additional fields and return options
8+
* @returns The inserted user object
9+
*/
10+
export async function insertUser(
11+
client: PgTestClient,
12+
email: string,
13+
options?: {
14+
id?: string;
15+
returnFields?: string[];
16+
}
17+
): Promise<{ id: string; email: string; [key: string]: any }> {
18+
const returnFields = options?.returnFields || ['id', 'email'];
19+
const fields = returnFields.join(', ');
20+
21+
if (options?.id) {
22+
return await client.one(
23+
`INSERT INTO auth.users (id, email)
24+
VALUES ($1, $2)
25+
RETURNING ${fields}`,
26+
[options.id, email]
27+
);
28+
} else {
29+
return await client.one(
30+
`INSERT INTO auth.users (id, email)
31+
VALUES (gen_random_uuid(), $1)
32+
RETURNING ${fields}`,
33+
[email]
34+
);
35+
}
36+
}

0 commit comments

Comments
 (0)