mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 12:25:05 +03:00
readme
This commit is contained in:
1 parent
59af01a7a9
commit
146c22ee72
2 files changed
+171
-1
No files matched your search
@@ -189,7 +189,7 @@ export const templates: Template[] = [
|
||||
"content": "insert into public.role_permissions (role, permission)\nvalues\n ('owner', 'organizations.read'),\n ('owner', 'organizations.update'),\n ('owner', 'organizations.delete'),\n ('owner', 'members.read'),\n ('owner', 'members.invite'),\n ('owner', 'members.update'),\n ('owner', 'members.remove'),\n ('owner', 'projects.read'),\n ('owner', 'projects.create'),\n ('owner', 'projects.update'),\n ('owner', 'projects.delete'),\n ('admin', 'organizations.read'),\n ('admin', 'organizations.update'),\n ('admin', 'members.read'),\n ('admin', 'members.invite'),\n ('admin', 'projects.read'),\n ('admin', 'projects.create'),\n ('admin', 'projects.update'),\n ('admin', 'projects.delete'),\n ('member', 'organizations.read'),\n ('member', 'members.read'),\n ('member', 'projects.read'),\n ('member', 'projects.create'),\n ('member', 'projects.update')\non conflict do nothing;\n"
|
||||
}
|
||||
],
|
||||
"readme": "# Multi-tenant RBAC template\n\nOrganization-scoped authorization for SaaS applications. Membership in an organization assigns a role, roles grant permissions, and RLS policies call `authorize(organization_id, permission)`.\n\n## Includes\n\n- Organizations and organization memberships\n- Organization-scoped roles and permissions\n- `create_organization()` bootstrap helper\n- `authorize()` helper for tenant-aware RLS policies\n- Example `projects` table with scalable tenant policies\n\n## Dependencies\n\nRequires **database** and **auth**."
|
||||
"readme": "# Multi-tenant RBAC template\n\nOrganization-scoped authorization for SaaS applications. Membership in an organization assigns a role, roles grant permissions, and RLS policies call `authorize(organization_id, permission)`.\n\nUse this template when users can belong to more than one organization and their permissions should be different in each organization.\n\n## Includes\n\n- Organizations and organization memberships\n- Organization-scoped roles and permissions\n- `create_organization()` bootstrap helper\n- `authorize()` helper for tenant-aware RLS policies\n- Example `projects` table with scalable tenant policies\n\n## How it works\n\nEvery tenant-owned row stores an `organization_id`. The current user's role for that organization is stored in `organization_members`, and each role receives permissions through `role_permissions`.\n\nRLS policies should check both tenant membership and the requested action by calling:\n\n```sql\npublic.authorize(organization_id, 'resource.action')\n```\n\nThat helper returns `true` only when the signed-in user is a member of the row's organization and their role grants the requested permission.\n\n## Getting started\n\nCreate an organization from your app after the user signs in:\n\n```sql\nselect public.create_organization('Acme Inc', 'acme');\n```\n\nThe creator is automatically inserted into `organization_members` as the `owner`.\n\nInvite another user by inserting a membership row:\n\n```sql\ninsert into public.organization_members (organization_id, user_id, role)\nvalues (\n '00000000-0000-0000-0000-000000000000',\n '11111111-1111-1111-1111-111111111111',\n 'member'\n);\n```\n\nThe insert is allowed only if the current user has `members.invite` for that organization. Assigning elevated roles such as `admin` or `owner` requires `members.update`.\n\n## Default roles\n\nThe seed file grants these permissions:\n\n| Role | Intended use |\n| ---- | ------------ |\n| `owner` | Full organization, membership, and project control. |\n| `admin` | Manage organization settings, invite members, and manage projects. |\n| `member` | Read organization and membership data, and create/update projects. |\n\nAdjust `supabase/seed.sql` before production if your app needs a stricter default. For example, remove `projects.create` or `projects.update` from `member` for read-only members.\n\n## Writing RLS policies\n\nFor tenant-owned tables, add an `organization_id` column and enable RLS:\n\n```sql\ncreate table public.documents (\n id uuid primary key default gen_random_uuid(),\n organization_id uuid references public.organizations on delete cascade not null,\n title text not null,\n body text,\n created_at timestamptz default now()\n);\n\ncreate index documents_organization_id_idx\non public.documents (organization_id);\n\nalter table public.documents enable row level security;\n```\n\nAdd permissions for the new resource:\n\n```sql\nalter type public.app_permission add value 'documents.read';\nalter type public.app_permission add value 'documents.create';\nalter type public.app_permission add value 'documents.update';\nalter type public.app_permission add value 'documents.delete';\n\ninsert into public.role_permissions (role, permission)\nvalues\n ('owner', 'documents.read'),\n ('owner', 'documents.create'),\n ('owner', 'documents.update'),\n ('owner', 'documents.delete'),\n ('admin', 'documents.read'),\n ('admin', 'documents.create'),\n ('admin', 'documents.update'),\n ('admin', 'documents.delete'),\n ('member', 'documents.read')\non conflict do nothing;\n```\n\nThen write one policy per operation:\n\n```sql\ncreate policy \"Authorized members can read documents\"\non public.documents\nfor select\nto authenticated\nusing ((select public.authorize(organization_id, 'documents.read')));\n\ncreate policy \"Authorized members can create documents\"\non public.documents\nfor insert\nto authenticated\nwith check ((select public.authorize(organization_id, 'documents.create')));\n\ncreate policy \"Authorized members can update documents\"\non public.documents\nfor update\nto authenticated\nusing ((select public.authorize(organization_id, 'documents.update')))\nwith check ((select public.authorize(organization_id, 'documents.update')));\n\ncreate policy \"Authorized members can delete documents\"\non public.documents\nfor delete\nto authenticated\nusing ((select public.authorize(organization_id, 'documents.delete')));\n```\n\nUse `with check` on inserts and updates so users cannot create or move rows into organizations where they do not have permission.\n\n## Common policy patterns\n\nRead access for any organization member:\n\n```sql\nusing ((select public.authorize(organization_id, 'documents.read')))\n```\n\nWrite access for admins and owners only:\n\n```sql\nwith check ((select public.authorize(organization_id, 'documents.create')))\n```\n\nOwner-only access can be modeled as a permission that only `owner` receives:\n\n```sql\nalter type public.app_permission aLine truncated
|
||||
},
|
||||
{
|
||||
"id": "auth-email",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Organization-scoped authorization for SaaS applications. Membership in an organization assigns a role, roles grant permissions, and RLS policies call `authorize(organization_id, permission)`.
|
||||
|
||||
Use this template when users can belong to more than one organization and their permissions should be different in each organization.
|
||||
|
||||
## Includes
|
||||
|
||||
- Organizations and organization memberships
|
||||
@@ -10,6 +12,174 @@ Organization-scoped authorization for SaaS applications. Membership in an organi
|
||||
- `authorize()` helper for tenant-aware RLS policies
|
||||
- Example `projects` table with scalable tenant policies
|
||||
|
||||
## How it works
|
||||
|
||||
Every tenant-owned row stores an `organization_id`. The current user's role for that organization is stored in `organization_members`, and each role receives permissions through `role_permissions`.
|
||||
|
||||
RLS policies should check both tenant membership and the requested action by calling:
|
||||
|
||||
```sql
|
||||
public.authorize(organization_id, 'resource.action')
|
||||
```
|
||||
|
||||
That helper returns `true` only when the signed-in user is a member of the row's organization and their role grants the requested permission.
|
||||
|
||||
## Getting started
|
||||
|
||||
Create an organization from your app after the user signs in:
|
||||
|
||||
```sql
|
||||
select public.create_organization('Acme Inc', 'acme');
|
||||
```
|
||||
|
||||
The creator is automatically inserted into `organization_members` as the `owner`.
|
||||
|
||||
Invite another user by inserting a membership row:
|
||||
|
||||
```sql
|
||||
insert into public.organization_members (organization_id, user_id, role)
|
||||
values (
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
'member'
|
||||
);
|
||||
```
|
||||
|
||||
The insert is allowed only if the current user has `members.invite` for that organization. Assigning elevated roles such as `admin` or `owner` requires `members.update`.
|
||||
|
||||
## Default roles
|
||||
|
||||
The seed file grants these permissions:
|
||||
|
||||
| Role | Intended use |
|
||||
| ---- | ------------ |
|
||||
| `owner` | Full organization, membership, and project control. |
|
||||
| `admin` | Manage organization settings, invite members, and manage projects. |
|
||||
| `member` | Read organization and membership data, and create/update projects. |
|
||||
|
||||
Adjust `supabase/seed.sql` before production if your app needs a stricter default. For example, remove `projects.create` or `projects.update` from `member` for read-only members.
|
||||
|
||||
## Writing RLS policies
|
||||
|
||||
For tenant-owned tables, add an `organization_id` column and enable RLS:
|
||||
|
||||
```sql
|
||||
create table public.documents (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
organization_id uuid references public.organizations on delete cascade not null,
|
||||
title text not null,
|
||||
body text,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
|
||||
create index documents_organization_id_idx
|
||||
on public.documents (organization_id);
|
||||
|
||||
alter table public.documents enable row level security;
|
||||
```
|
||||
|
||||
Add permissions for the new resource:
|
||||
|
||||
```sql
|
||||
alter type public.app_permission add value 'documents.read';
|
||||
alter type public.app_permission add value 'documents.create';
|
||||
alter type public.app_permission add value 'documents.update';
|
||||
alter type public.app_permission add value 'documents.delete';
|
||||
|
||||
insert into public.role_permissions (role, permission)
|
||||
values
|
||||
('owner', 'documents.read'),
|
||||
('owner', 'documents.create'),
|
||||
('owner', 'documents.update'),
|
||||
('owner', 'documents.delete'),
|
||||
('admin', 'documents.read'),
|
||||
('admin', 'documents.create'),
|
||||
('admin', 'documents.update'),
|
||||
('admin', 'documents.delete'),
|
||||
('member', 'documents.read')
|
||||
on conflict do nothing;
|
||||
```
|
||||
|
||||
Then write one policy per operation:
|
||||
|
||||
```sql
|
||||
create policy "Authorized members can read documents"
|
||||
on public.documents
|
||||
for select
|
||||
to authenticated
|
||||
using ((select public.authorize(organization_id, 'documents.read')));
|
||||
|
||||
create policy "Authorized members can create documents"
|
||||
on public.documents
|
||||
for insert
|
||||
to authenticated
|
||||
with check ((select public.authorize(organization_id, 'documents.create')));
|
||||
|
||||
create policy "Authorized members can update documents"
|
||||
on public.documents
|
||||
for update
|
||||
to authenticated
|
||||
using ((select public.authorize(organization_id, 'documents.update')))
|
||||
with check ((select public.authorize(organization_id, 'documents.update')));
|
||||
|
||||
create policy "Authorized members can delete documents"
|
||||
on public.documents
|
||||
for delete
|
||||
to authenticated
|
||||
using ((select public.authorize(organization_id, 'documents.delete')));
|
||||
```
|
||||
|
||||
Use `with check` on inserts and updates so users cannot create or move rows into organizations where they do not have permission.
|
||||
|
||||
## Common policy patterns
|
||||
|
||||
Read access for any organization member:
|
||||
|
||||
```sql
|
||||
using ((select public.authorize(organization_id, 'documents.read')))
|
||||
```
|
||||
|
||||
Write access for admins and owners only:
|
||||
|
||||
```sql
|
||||
with check ((select public.authorize(organization_id, 'documents.create')))
|
||||
```
|
||||
|
||||
Owner-only access can be modeled as a permission that only `owner` receives:
|
||||
|
||||
```sql
|
||||
alter type public.app_permission add value 'billing.manage';
|
||||
|
||||
insert into public.role_permissions (role, permission)
|
||||
values ('owner', 'billing.manage')
|
||||
on conflict do nothing;
|
||||
```
|
||||
|
||||
Then use it in a policy:
|
||||
|
||||
```sql
|
||||
using ((select public.authorize(organization_id, 'billing.manage')))
|
||||
```
|
||||
|
||||
## Choosing permissions
|
||||
|
||||
Prefer permissions that describe product actions rather than database internals:
|
||||
|
||||
```text
|
||||
projects.read
|
||||
projects.create
|
||||
projects.update
|
||||
projects.delete
|
||||
members.invite
|
||||
billing.manage
|
||||
```
|
||||
|
||||
This keeps policies stable when the schema changes and makes it easier to map UI actions to backend access.
|
||||
|
||||
## Notes
|
||||
|
||||
This template does not rely on JWT custom claims for authorization. The database checks the current user's organization membership at query time, so a user can safely have different roles in different organizations.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Requires **database** and **auth**.
|
||||
Reference in new issue
Block a user