Skip to content

Commit

Permalink
docs: replace pgpolicy with crudpolicy
Browse files Browse the repository at this point in the history
  • Loading branch information
dhanushreddy291 committed Nov 20, 2024
1 parent 0eac732 commit 8c00266
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 168 deletions.
69 changes: 28 additions & 41 deletions content/docs/guides/neon-authorize-auth0.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@ Below are examples of RLS policies for a **todos** table, designed to restrict a

```typescript shouldWrap
import { InferSelectModel, sql } from 'drizzle-orm';
import { bigint, boolean, pgPolicy, pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { bigint, boolean, pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { authenticatedRole, authUid, crudPolicy } from 'drizzle-orm/neon';

// schema for TODOs table
export const todos = pgTable(
'todos',
{
Expand All @@ -162,31 +164,14 @@ export const todos = pgTable(
isComplete: boolean('is_complete').notNull().default(false),
insertedAt: timestamp('inserted_at', { withTimezone: true }).defaultNow().notNull(),
},
(t) => ({
p1: pgPolicy('create todos', {
for: 'insert',
to: 'authenticated',
withCheck: sql`(select auth.user_id() = user_id)`,
// Create RLS policy for the table
(table) => [
crudPolicy({
role: authenticatedRole,
read: authUid(table.userId),
modify: authUid(table.userId),
}),

p2: pgPolicy('view todos', {
for: 'select',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),

p3: pgPolicy('update todos', {
for: 'update',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),

p4: pgPolicy('delete todos', {
for: 'delete',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),
})
]
);

export type Todo = InferSelectModel<typeof todos>;
Expand All @@ -210,27 +195,29 @@ CREATE TABLE todos (
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;

-- 2nd create policies for your table
CREATE POLICY "Individuals can create todos." ON todos FOR INSERT
TO authenticated
WITH CHECK ((select auth.user_id()) = user_id);

CREATE POLICY "Individuals can view their own todos. " ON todos FOR SELECT
TO authenticated
USING ((select auth.user_id()) = user_id);

CREATE POLICY "Individuals can update their own todos." ON todos FOR UPDATE
TO authenticated
USING ((select auth.user_id()) = user_id)
WITH CHECK ((select auth.user_id()) = user_id);

CREATE POLICY "Individuals can delete their own todos." ON todos FOR DELETE
TO authenticated
USING ((select auth.user_id()) = user_id);
CREATE POLICY "crud-authenticated-policy-select" ON "todos" AS PERMISSIVE FOR SELECT
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-insert" ON "todos" AS PERMISSIVE FOR INSERT
TO "authenticated"
WITH CHECK ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-update" ON "todos" AS PERMISSIVE FOR UPDATE
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"))
WITH CHECK ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-delete" ON "todos" AS PERMISSIVE FOR DELETE
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"));
```

</TabItem>
</Tabs>

The `crudPolicy` function simplifies policy creation by generating all necessary CRUD policies with a single declaration.

### 2. Run your first authorized query

With RLS policies in place, you can now query the database using JWTs from Auth0 , restricting access based on the user's identity. Here are examples of how you could run authenticated queries from both the backend and the frontend of our sample **todos** application. Highlighted lines in the code samples emphasize key actions related to authentication and querying.
Expand Down
178 changes: 94 additions & 84 deletions content/docs/guides/neon-authorize-clerk.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,29 @@ CREATE EXTENSION IF NOT EXISTS pg_session_jwt;
The integration creates the `authenticated` and `anonymous` roles for you. Let's define table-level permissions for these roles. To allow both roles to read and write to tables in your public schema, run:

```sql
GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA public TO authenticated;
GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA public TO anonymous;
-- For existing tables
GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES
IN SCHEMA public
to authenticated;

GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES
IN SCHEMA public
to anonymous;

-- For future tables
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES
TO authenticated;

ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES
TO anonymous;

-- Grant USAGE on "public" schema
GRANT USAGE ON SCHEMA public TO authenticated;
GRANT USAGE ON SCHEMA public TO anonymous;
```

- **Authenticated role**: This role is intended for users who are logged in. Your application should send the authorization token when connecting using this role.
Expand Down Expand Up @@ -131,7 +152,8 @@ Here are some examples of RLS policies for a **todos** table, designed to restri

```typescript
import { InferSelectModel, sql } from 'drizzle-orm';
import { bigint, boolean, pgPolicy, pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { bigint, boolean, pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { authenticatedRole, authUid, crudPolicy } from 'drizzle-orm/neon';

// schema for TODOs table
export const todos = pgTable(
Expand All @@ -145,32 +167,14 @@ export const todos = pgTable(
isComplete: boolean('is_complete').notNull().default(false),
insertedAt: timestamp('inserted_at', { withTimezone: true }).defaultNow().notNull(),
},
// Create policies for your table
(t) => ({
p1: pgPolicy('create todos', {
for: 'insert',
to: 'authenticated',
withCheck: sql`(select auth.user_id() = user_id)`,
// Create RLS policy for the table
(table) => [
crudPolicy({
role: authenticatedRole,
read: authUid(table.userId),
modify: authUid(table.userId),
}),

p2: pgPolicy('view todos', {
for: 'select',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),

p3: pgPolicy('update todos', {
for: 'update',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),

p4: pgPolicy('delete todos', {
for: 'delete',
to: 'authenticated',
using: sql`(select auth.user_id() = user_id)`,
}),
})
]
);

export type Todo = InferSelectModel<typeof todos>;
Expand All @@ -194,23 +198,30 @@ CREATE TABLE todos (
alter table todos enable row level security;

-- 2nd create policies for your table
create policy "Individuals can create todos." on todos for
insert with check (auth.user_id() = user_id);

create policy "Individuals can view their own todos." on todos for
select using (auth.user_id() = user_id);

create policy "Individuals can update their own todos." on todos for
update using (auth.user_id() = user_id);

create policy "Individuals can delete their own todos." on todos for
delete using (auth.user_id() = user_id);
CREATE POLICY "crud-authenticated-policy-select" ON "todos" AS PERMISSIVE FOR SELECT
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-insert" ON "todos" AS PERMISSIVE FOR INSERT
TO "authenticated"
WITH CHECK ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-update" ON "todos" AS PERMISSIVE FOR UPDATE
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"))
WITH CHECK ((select auth.user_id() = "todos"."user_id"));

CREATE POLICY "crud-authenticated-policy-delete" ON "todos" AS PERMISSIVE FOR DELETE
TO "authenticated"
USING ((select auth.user_id() = "todos"."user_id"));
```

</TabItem>

</Tabs>

The `crudPolicy` function simplifies policy creation by generating all necessary CRUD policies with a single declaration.

### 2. Run your first authorized query

With Row-Level Security (RLS) policies in place, you can securely query the database using JWTs from Clerk, restricting access based on each user’s identity. Here are examples of running authenticated queries from both the backend and frontend of our sample **todos** application. Highlighted lines in the code samples emphasize key actions related to authentication and querying.
Expand Down Expand Up @@ -257,51 +268,50 @@ export async function TodoList() {
```typescript shouldWrap
'use client';

import type { Todo } from '@/app/schema';
import { neon } from '@neondatabase/serverless';
import { useAuth } from '@clerk/nextjs';
import { useEffect, useState } from 'react';

const getDb = (token: string) =>
neon(process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL!, {
authToken: token, // [!code highlight]
});

export function TodoList() {
const { getToken } = useAuth();
const [todos, setTodos] = useState<Array<Todo>>();

useEffect(() => {
async function loadTodos() {
const authToken = await getToken(); // [!code highlight]

if (!authToken) {
return;
}

const sql = getDb(authToken);

// WHERE filter is optional because of RLS.
// But we send it anyway for performance reasons.
const todosResponse = await
sql('select * from todos where user_id = auth.user_id()'); // [!code highlight]

setTodos(todosResponse as Array<Todo>);
}

loadTodos();
}, [getToken]);

return (
<ul>
{todos?.map((todo) => (
<li key={todo.id}>
{todo.task}
</li>
))}
</ul>
);
}
import type { Todo } from '@/app/schema';
import { neon } from '@neondatabase/serverless';
import { useAuth } from '@clerk/nextjs';
import { useEffect, useState } from 'react';

const getDb = (token: string) =>
neon(process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL!, {
authToken: token, // [!code highlight]
});

export function TodoList() {
const { getToken } = useAuth();
const [todos, setTodos] = useState<Array<Todo>>();

useEffect(() => {
async function loadTodos() {
const authToken = await getToken(); // [!code highlight]

if (!authToken) {
return;
}

const sql = getDb(authToken);

// WHERE filter is optional because of RLS.
// But we send it anyway for performance reasons.
const todosResponse = await sql('select * from todos where user_id = auth.user_id()'); // [!code highlight]

setTodos(todosResponse as Array<Todo>);
}

loadTodos();
}, [getToken]);

return (
<ul>
{todos?.map((todo) => (
<li key={todo.id}>
{todo.task}
</li>
))}
</ul>
);
}
```

</TabItem>
Expand Down
Loading

0 comments on commit 8c00266

Please sign in to comment.