Skip to main content

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​

  1. These rules implement a hierarchical access system where higher-level roles have access to all lower-level content.
  2. Entity names are hidden in OpportunityDetails and Opportunities for subscribers.
  3. Only confidential users and admins can see confidential information such as Entity name and location in both Entities and Opportunities.
  4. Ensure that user roles are correctly set in authentication tokens when users sign up or their roles change.
  5. Always test these rules thoroughly to ensure they provide the correct level of access for each user type and protect your data as intended.