mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 10:59:39 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
198 lines
6.1 KiB
Markdown
198 lines
6.1 KiB
Markdown
# RLS Implementation Guide for Maerchenzauber Storyteller
|
|
|
|
## Overview
|
|
|
|
This document describes the Row Level Security (RLS) implementation for the Maerchenzauber Storyteller project, based on proven patterns from the Memoro project. The implementation provides secure, user-based access control for both database tables and storage buckets.
|
|
|
|
## Implemented Changes
|
|
|
|
### 1. Core Authentication Functions
|
|
|
|
We've created four essential authentication helper functions:
|
|
|
|
#### `current_user_id()`
|
|
|
|
- **Purpose**: Extracts user identity from JWT claims
|
|
- **Returns**: User ID as text
|
|
- **Usage**: Primary function for identifying the current user in RLS policies
|
|
|
|
#### `auth_is_admin()`
|
|
|
|
- **Purpose**: Checks if the current user has admin privileges
|
|
- **Returns**: Boolean (true if admin, false otherwise)
|
|
- **Usage**: Used in policies that require admin access (creators, errors tables)
|
|
|
|
#### `current_user_uuid()`
|
|
|
|
- **Purpose**: Returns the current user's ID as UUID
|
|
- **Returns**: UUID
|
|
- **Usage**: Compatibility function when UUID type is needed
|
|
|
|
#### `user_owns_resource(resource_user_id text)`
|
|
|
|
- **Purpose**: Helper function to check resource ownership
|
|
- **Returns**: Boolean
|
|
- **Usage**: Simplifies ownership checks in policies
|
|
|
|
### 2. Table RLS Policies
|
|
|
|
#### Characters Table
|
|
|
|
- **View**: Users can only see their own characters
|
|
- **Insert**: Users can only create characters with their user_id
|
|
- **Update**: Users can only update their own characters
|
|
- **Delete**: Users can only delete their own characters
|
|
- **Service Role**: Full access for backend operations
|
|
|
|
#### Stories Table
|
|
|
|
- **View**: Users can only see their own stories
|
|
- **Insert**: Users can only create stories with their user_id
|
|
- **Update**: Users can only update their own stories
|
|
- **Delete**: Users can only delete their own stories
|
|
- **Service Role**: Full access for backend operations
|
|
|
|
#### Creators Table
|
|
|
|
- **View**: All authenticated users can view creators
|
|
- **Insert/Update/Delete**: Only admins can modify creators
|
|
- **Service Role**: Full access for backend operations
|
|
|
|
#### Errors Table
|
|
|
|
- **View**: Only admins can view errors
|
|
- **Insert**: Authenticated users can insert their own errors (for logging)
|
|
- **Update/Delete**: Only admins can modify errors
|
|
- **Service Role**: Full access for backend operations
|
|
|
|
### 3. Storage RLS Policies
|
|
|
|
#### storyteller-images bucket (public)
|
|
|
|
- **Structure**: `{user_id}/stories/{story_id}/` or `{user_id}/characters/{character_id}/`
|
|
- **View**: Anyone can view images (public bucket)
|
|
- **Upload/Update/Delete**: Users can only manage files in their own folder (first folder must match their user_id)
|
|
- **Admin Access**: Admins can view and delete any file
|
|
|
|
#### user-uploads bucket (private)
|
|
|
|
- **Structure**: `{user_id}/{filename}`
|
|
- **View/Upload/Update/Delete**: Users can only access their own folder
|
|
- **Admin Access**: Admins can view and delete any file
|
|
|
|
### 4. Migration Files Created
|
|
|
|
1. **auth-functions.sql**: Core authentication functions
|
|
2. **update-rls-policies.sql**: Updated RLS policies using new functions
|
|
3. **storage-rls-policies.sql**: Storage bucket policies
|
|
4. **cleanup-duplicate-policies.sql**: Removed duplicate policies
|
|
5. **test-rls-policies.sql**: Comprehensive test queries
|
|
|
|
## Usage in Application Code
|
|
|
|
### TypeScript/JavaScript Example
|
|
|
|
```typescript
|
|
// File upload with proper path structure
|
|
const uploadFile = async (file: File, userId: string, storyId: string) => {
|
|
const filePath = `${userId}/stories/${storyId}/${Date.now()}_${file.name}`;
|
|
|
|
const { data, error } = await supabase.storage.from('storyteller-images').upload(filePath, file);
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
};
|
|
|
|
// Fetching user's own data (RLS automatically filters)
|
|
const getUserCharacters = async () => {
|
|
const { data, error } = await supabase.from('characters').select('*');
|
|
|
|
// Only returns characters where user_id matches current user
|
|
return data;
|
|
};
|
|
```
|
|
|
|
### Admin Operations
|
|
|
|
```typescript
|
|
// Check if user is admin
|
|
const checkAdminStatus = async () => {
|
|
const { data, error } = await supabase.rpc('auth_is_admin');
|
|
|
|
return data; // true or false
|
|
};
|
|
|
|
// Admin creating a creator (requires admin role)
|
|
const createCreator = async (creatorData: CreatorInput) => {
|
|
const { data, error } = await supabase.from('creators').insert(creatorData);
|
|
|
|
// Will fail if user is not admin
|
|
return data;
|
|
};
|
|
```
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Always use auth functions**: Use `current_user_id()` instead of direct JWT manipulation
|
|
2. **Test policies thoroughly**: Use the test-rls-policies.sql file to verify access
|
|
3. **Service role key protection**: Never expose service role key in client code
|
|
4. **Consistent user identification**: Always store user_id as text for compatibility
|
|
5. **Storage path structure**: Follow the defined folder structure for storage
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Access Denied Errors**
|
|
- Verify user is authenticated
|
|
- Check if user_id matches the resource owner
|
|
- Ensure RLS is enabled on the table
|
|
|
|
2. **Admin Functions Not Working**
|
|
- Verify user has admin flag in users table
|
|
- Check JWT claims include proper app_access data
|
|
- Ensure auth_is_admin() function is working correctly
|
|
|
|
3. **Storage Upload Failures**
|
|
- Verify file path follows the required structure
|
|
- Check bucket exists and RLS is enabled
|
|
- Ensure user is authenticated
|
|
|
|
### Debug Queries
|
|
|
|
```sql
|
|
-- Check current user
|
|
SELECT current_user_id(), auth_is_admin();
|
|
|
|
-- View active policies
|
|
SELECT * FROM pg_policies WHERE tablename = 'your_table';
|
|
|
|
-- Test specific access
|
|
SET request.jwt.claim.sub = 'test-user-id';
|
|
SELECT * FROM characters;
|
|
```
|
|
|
|
## Maintenance
|
|
|
|
### Adding New Tables
|
|
|
|
When adding new tables with RLS:
|
|
|
|
1. Enable RLS: `ALTER TABLE new_table ENABLE ROW LEVEL SECURITY;`
|
|
2. Create policies using auth functions
|
|
3. Add service role policy
|
|
4. Test all CRUD operations
|
|
5. Document the policies
|
|
|
|
### Updating Policies
|
|
|
|
1. Always drop old policy before creating new one
|
|
2. Use migrations for version control
|
|
3. Test changes thoroughly
|
|
4. Update documentation
|
|
|
|
## References
|
|
|
|
- [Supabase RLS Documentation](https://supabase.com/docs/guides/auth/row-level-security)
|
|
- [PostgreSQL RLS Guide](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
|
|
- Original Memoro RLS Implementation Guide
|