Firestore Rules Reference
This document provides a comprehensive reference for the Firestore security rules used in the Ring App project.
Overviewβ
These rules implement a granular access control system based on user roles and content visibility. The main roles are:
- Admin
- Classified (VIP)
- Member
- Subscriber
- Unauthenticated
Content visibility levels:
- Public
- Subscriber
- Members Only
- Classified
Helper Functionsβ
User Role Checksβ
function isAdmin() {
return request.auth != null && request.auth.token.role == 'admin';
}
function isAuthenticated() {
return request.auth != null;
}
function isClassified() {
return request.auth != null && request.auth.token.role == 'confidential';
}
function isMember() {
return request.auth != null && (request.auth.token.role == 'member' || request.auth.token.role == 'confidential' || isAdmin());
}
function isSubscriber() {
return request.auth != null && (request.auth.token.role == 'subscriber' || isMember());
}
Visibility Access Checkβ
function canAccessVisibility(visibility) {
return (visibility == 'public') ||
(visibility == 'subscriber' && isSubscriber()) ||
(visibility == 'member' && isMember()) ||
(visibility == 'confidential' && isClassified()) ||
isAdmin();
}
Collection Rulesβ
Opportunities Collectionβ
match /opportunities/{opportunityId} {
// Allow read based on visibility and user role
allow read: if canAccessVisibility(resource.data.visibility);
// Function to determine which fields a user can access
function getAccessibleFields() {
let baseFields = ['id', 'type', 'title', 'briefDescription', 'category', 'tags', 'visibility'];
let memberFields = baseFields.concat(['fullDescription', 'createdBy', 'dateCreated', 'dateUpdated', 'expirationDate', 'status', 'location', 'budget', 'requiredSkills', 'requiredDocuments', 'attachments']);
let confidentialFields = memberFields.concat(['organizationId', 'contactInfo']);
return isClassified() || isAdmin() ? confidentialFields :
isMember() ? memberFields :
isSubscriber() ? baseFields :
[];
}
// Allow create with proper fields and visibility
allow create: if isAuthenticated()
&& request.resource.data.visibility in ['public', 'subscriber', 'member', 'confidential']
&& request.resource.data.keys().hasAll(['title', 'briefDescription', 'fullDescription', 'createdBy', 'organizationId', 'dateCreated', 'dateUpdated', 'expirationDate', 'status', 'type', 'category', 'tags', 'location', 'visibility']);
// Allow update with proper fields and visibility
allow update: if (request.auth.uid == resource.data.createdBy || isAdmin())
&& request.resource.data.visibility in ['public', 'subscriber', 'member', 'confidential']
&& request.resource.data.keys().hasAll(['title', 'briefDescription', 'fullDescription', 'createdBy', 'organizationId', 'dateCreated', 'dateUpdated', 'expirationDate', 'status', 'type', 'category', 'tags', 'location', 'visibility']);
// Allow delete only by creator or admin
allow delete: if request.auth.uid == resource.data.createdBy || isAdmin();
// Allow read only for accessible fields
allow read: if request.resource.data.keys().hasOnly(getAccessibleFields());
}
Access Levels for Opportunitiesβ
- Classified Users and Admins: Can see all information.
- Members: Can see opportunity details, types, tags, categories, documents, and author name.
- Subscribers: Can only view opportunity name, briefDescription, category, and tags.
- Unauthenticated: Can only see public opportunities with limited information.
Entities Collectionβ
match /entities/{entityId} {
// Function to determine which fields a user can access
function getAccessibleFields() {
let baseFields = ['id', 'type', 'shortDescription', 'tags', 'visibility'];
let memberFields = baseFields.concat(['name', 'fullDescription', 'logo', 'website', 'contactEmail', 'phoneNumber', 'socialMedia', 'foundedYear', 'employeeCount', 'industries', 'services', 'certifications', 'partnerships', 'upcomingEvents', 'gallery']);
let confidentialFields = memberFields.concat(['location', 'addedBy', 'dateAdded', 'lastUpdated', 'memberSince']);
return isClassified() || isAdmin() ? confidentialFields :
isMember() ? memberFields :
isSubscriber() ? baseFields :
[];
}
// Allow read based on visibility and accessible fields
allow read: if canAccessVisibility(resource.data.visibility)
&& request.resource.data.keys().hasOnly(getAccessibleFields());
// Allow create and update only by admin with all required fields
allow create, update: if isAdmin()
&& request.resource.data.keys().hasAll(['name', 'type', 'shortDescription', 'location', 'visibility', 'dateAdded', 'lastUpdated'])
&& request.resource.data.visibility in ['public', 'subscriber', 'member', 'confidential'];
// Allow delete only by admin
allow delete: if isAdmin();
}
Access Levels for Entitiesβ
- Classified Users and Admins: Can see all information, including name and location.
- Members: Can see everything except location and some administrative fields.
- Subscribers: Can only see basic information (id, type, shortDescription, tags, visibility).
- Unauthenticated: Can only see public entities with limited information.
User Profiles Collectionβ
match /userProfiles/{userId} {
allow read: if isAuthenticated() && (request.auth.uid == userId || isAdmin());
allow create, update: if isAuthenticated() && request.auth.uid == userId
&& request.resource.data.keys().hasAll(['email', 'name', 'role', 'organizationId', 'memberSince', 'lastLogin', 'isVerified'])
&& request.resource.data.email == request.auth.token.email;
allow delete: if isAdmin();
}
User Settings Collectionβ
match /userSettings/{userId} {
allow read, write: if request.auth.uid == userId || isAdmin();
}
Contact Forms Collectionβ
match /contactForms/{formId} {
allow create: if request.resource.data.keys().hasAll(['name', 'email', 'message']);
allow read, update, delete: if isAdmin();
}
Chats Collectionβ
match /chats/{chatId} {
allow read, write: if request.auth.uid in resource.data.participants || isAdmin();
}
Important Notesβ
- These rules implement a hierarchical access system where higher-level roles have access to all lower-level content.
- Entity names are hidden in OpportunityDetails and Opportunities for subscribers.
- Only confidential users and admins can see confidential information such as Entity name and location in both Entities and Opportunities.
- Ensure that user roles are correctly set in authentication tokens when users sign up or their roles change.
- Always test these rules thoroughly to ensure they provide the correct level of access for each user type and protect your data as intended.