217 lines
9.1 KiB
Plaintext
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;
|
|
}
|
|
}
|
|
}
|
|
|