Files
vibn-frontend/firestore.rules

217 lines
9.1 KiB
Plaintext

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Users collection
match /users/{userId} {
// Users can read their own data
allow read: if isOwner(userId);
// Users can create their own user document
allow create: if isOwner(userId);
// Users can update their own data
allow update: if isOwner(userId);
// No deletes for now
allow delete: if false;
}
// API Keys collection
match /apiKeys/{keyId} {
// Only the server can create/read API keys (via Admin SDK)
// Users cannot directly access API key documents
allow read, write: if false;
}
// MCP API Keys collection
match /mcpKeys/{keyId} {
// Only the server can create/read/delete MCP keys (via Admin SDK)
// Users cannot directly access MCP key documents
allow read, write: if false;
}
// Projects collection
match /projects/{projectId} {
// Users can read their own projects
allow read: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Users can create projects
allow create: if isAuthenticated() && request.resource.data.userId == request.auth.uid;
// Users can update their own projects
allow update: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Users can delete their own projects
allow delete: if isAuthenticated() && resource.data.userId == request.auth.uid;
// AI Conversations subcollection
match /aiConversations/{conversationId} {
// Users can read conversations for their projects
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(projectId)).data.userId == request.auth.uid;
// Server creates conversation entries via Admin SDK
allow create: if false; // Only server via Admin SDK
// No updates to conversation history (immutable)
allow update: if false;
// No deletes (audit trail)
allow delete: if false;
}
// Vision Board subcollection
match /visionBoard/{visionDocId} {
// Users can read/write vision board for their projects
allow read, write: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(projectId)).data.userId == request.auth.uid;
}
// Context Sources subcollection (for chat content, files, etc.)
match /contextSources/{sourceId} {
// Users can read/write context sources for their projects
allow read, write: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(projectId)).data.userId == request.auth.uid;
}
}
// Sessions collection
match /sessions/{sessionId} {
// Users can read their own sessions (by userId or by projectId they own)
allow read: if isAuthenticated() && (
resource.data.userId == request.auth.uid ||
(resource.data.projectId != null &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid)
);
// Sessions are created by the server via API (Admin SDK)
allow create: if false; // Only server via Admin SDK
// Users can update their own sessions
allow update: if isAuthenticated() && resource.data.userId == request.auth.uid;
// No deletes for sessions (audit trail)
allow delete: if false;
}
// Analyses collection
match /analyses/{analysisId} {
// Users can read analyses for their projects
// Note: This requires fetching the project document to verify ownership
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// Users can create analyses for their projects
allow create: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(request.resource.data.projectId)).data.userId == request.auth.uid;
// Users can update analyses for their projects
allow update: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// No deletes for analyses (audit trail)
allow delete: if false;
}
// Work Completed collection
match /workCompleted/{workId} {
// Users can read work completed for their projects
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// Server creates work completed entries
allow create: if false; // Only server via Admin SDK
// Users can update work completed for their projects
allow update: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// No deletes
allow delete: if false;
}
// Clients collection
match /clients/{clientId} {
// Users can read their own clients
allow read: if isAuthenticated() && resource.data.ownerId == request.auth.uid;
// Users can create clients
allow create: if isAuthenticated() && request.resource.data.ownerId == request.auth.uid;
// Users can update their own clients
allow update: if isAuthenticated() && resource.data.ownerId == request.auth.uid;
// Users can delete their own clients
allow delete: if isAuthenticated() && resource.data.ownerId == request.auth.uid;
}
// ChatGPT Imports collection
match /chatgptImports/{importId} {
// Users can read their own imports
allow read: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Server creates imports via Admin SDK
allow create: if false; // Only server via Admin SDK
// Users can update their own imports (e.g., add notes)
allow update: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Users can delete their own imports
allow delete: if isAuthenticated() && resource.data.userId == request.auth.uid;
}
// User API Keys collection (third-party keys like OpenAI, GitHub)
match /userKeys/{keyId} {
// Only server can access keys (via Admin SDK)
// Keys are encrypted and should never be directly accessible to clients
allow read, write: if false;
}
// Knowledge Items collection (documents, notes, chat imports)
match /knowledge_items/{itemId} {
// Users can read knowledge items for their projects
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// Server creates knowledge items via Admin SDK
allow create: if false; // Only server via Admin SDK
// No updates or deletes (immutable)
allow update, delete: if false;
}
// Chat Extractions collection (AI-extracted insights)
match /chat_extractions/{extractionId} {
// Users can read extractions for their projects
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// Server creates extractions via Admin SDK
allow create: if false; // Only server via Admin SDK
// No updates or deletes (immutable)
allow update, delete: if false;
}
// Chat Conversations collection (conversation history)
match /chat_conversations/{conversationId} {
// Users can read conversations for their projects
allow read: if isAuthenticated() &&
get(/databases/$(database)/documents/projects/$(resource.data.projectId)).data.userId == request.auth.uid;
// Server creates and updates conversations via Admin SDK
allow create, update: if false; // Only server via Admin SDK
// No deletes (audit trail)
allow delete: if false;
}
// GitHub Connections collection (OAuth tokens and profile)
match /githubConnections/{connectionId} {
// Users can read their own GitHub connections
allow read: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Server creates connections via OAuth callback
allow create: if false; // Only server via Admin SDK
// Users cannot update or delete (managed by server)
allow update, delete: if false;
}
// Linked Extensions collection (browser extension connections)
match /linkedExtensions/{linkId} {
// Users can read their own extension links
allow read: if isAuthenticated() && resource.data.userId == request.auth.uid;
// Server creates links via API
allow create: if false; // Only server via Admin SDK
// No updates or deletes
allow update, delete: if false;
}
// Default deny all other access
match /{document=**} {
allow read, write: if false;
}
}
}