90 lines
3.0 KiB
Plaintext
90 lines
3.0 KiB
Plaintext
rules_version = '2';
|
|
service cloud.firestore {
|
|
match /databases/{database}/documents {
|
|
// ===============================================================
|
|
// Assumed Data Model
|
|
// ===============================================================
|
|
//
|
|
// Collection: users
|
|
// Document ID: {uid} (Firebase Auth UID)
|
|
// Fields:
|
|
// - name: string (required, 1-100 chars)
|
|
// - email: string (required, valid email format)
|
|
// - phone: string (required, 1-20 chars)
|
|
// - uid: string (required, matches document ID)
|
|
// - createdAt: string (required, ISO 8601 format)
|
|
//
|
|
// Collection: subscriptions
|
|
// Document ID: {uid} (Firebase Auth UID)
|
|
// Fields:
|
|
// - plan: string (required, enum: ['Aura Pro'])
|
|
// - topUpAmount: number (required, positive)
|
|
// - rechargeLevel: number (required, positive)
|
|
// - uid: string (required, matches document ID)
|
|
// - updatedAt: string (required, ISO 8601 format)
|
|
//
|
|
// ===============================================================
|
|
|
|
// ===============================================================
|
|
// Helper Functions
|
|
// ===============================================================
|
|
|
|
function isAuthenticated() {
|
|
return request.auth != null;
|
|
}
|
|
|
|
function isOwner(userId) {
|
|
return isAuthenticated() && request.auth.uid == userId;
|
|
}
|
|
|
|
function isValidEmail(email) {
|
|
return email is string && email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
|
|
}
|
|
|
|
function isValidDateString(dateStr) {
|
|
return dateStr is string && dateStr.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.*Z?$");
|
|
}
|
|
|
|
function hasOnlyAllowedFields(fields) {
|
|
return request.resource.data.keys().hasOnly(fields);
|
|
}
|
|
|
|
// Domain Validators
|
|
|
|
function isValidUser(data) {
|
|
return hasOnlyAllowedFields(['name', 'email', 'phone', 'uid', 'createdAt']) &&
|
|
data.name is string && data.name.size() > 0 && data.name.size() <= 100 &&
|
|
isValidEmail(data.email) &&
|
|
data.phone is string && data.phone.size() > 0 && data.phone.size() <= 20 &&
|
|
data.uid == request.auth.uid &&
|
|
isValidDateString(data.createdAt);
|
|
}
|
|
|
|
function isValidSubscription(data) {
|
|
return hasOnlyAllowedFields(['plan', 'topUpAmount', 'rechargeLevel', 'uid', 'updatedAt']) &&
|
|
data.plan in ['Aura Pro'] &&
|
|
data.topUpAmount is number && data.topUpAmount > 0 &&
|
|
data.rechargeLevel is number && data.rechargeLevel >= 0 &&
|
|
data.uid == request.auth.uid &&
|
|
isValidDateString(data.updatedAt);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Rules
|
|
// ===============================================================
|
|
|
|
match /users/{uid} {
|
|
allow read, write: if true;
|
|
}
|
|
|
|
match /subscriptions/{uid} {
|
|
allow read, write: if true;
|
|
}
|
|
|
|
// Default deny
|
|
match /{path=**} {
|
|
allow read, write: if false;
|
|
}
|
|
}
|
|
}
|