
{
"rules": {
".read": false, // Regra global de leitura
".write": false, // Regra global de escrita
"users": {
"$userId": {
".read": "auth != null && auth.uid == $userId",
".write": "auth != null && auth.uid == $userId",
".validate": "newData.hasChildren(['name', 'email'])"
}
}
}
}auth - Objeto com informações do usuário autenticadoauth.uid - ID único do usuárioauth.token - Claims customizados e provider infoauth.token.email - Email do usuárioauth.token.email_verified - Status de verificaçãodata - Dados atuais antes da operaçãonewData - Dados após a operação (em writes)root - Referência à raiz do banco$variables - Parâmetros de path dinâmicos{
"rules": {
"posts": {
"$postId": {
".read": true,
".write": "auth != null &&
(!data.exists() ||
data.child('authorId').val() == auth.uid)",
".validate": "newData.hasChildren(['title', 'body', 'authorId']) &&
newData.child('title').isString() &&
newData.child('body').isString() &&
newData.child('authorId').val() == auth.uid"
}
}
}
}{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}$uid é uma variável que captura o ID do path. A regra verifica se o usuário está autenticado (auth != null) e se o ID dele corresponde ao path sendo acessado.{
"rules": {
"publicPosts": {
".read": true,
"$postId": {
".write": "auth != null &&
(newData.child('authorId').val() == auth.uid ||
data.child('authorId').val() == auth.uid)",
".validate": "newData.hasChildren(['title', 'content', 'authorId', 'timestamp'])"
}
}
}
}isString(), isNumber(), isBoolean() e expressões regulares para validações complexas.{
"rules": {
"products": {
"$productId": {
".write": "auth != null",
".validate": "newData.hasChildren(['name', 'price', 'category']) &&
newData.child('name').isString() &&
newData.child('name').val().length > 3 &&
newData.child('name').val().length <= 100 &&
newData.child('price').isNumber() &&
newData.child('price').val() > 0 &&
newData.child('category').isString() &&
newData.child('category').val().matches(/^(electronics|clothing|books)$/)",
"name": { ".validate": "newData.isString()" },
"price": { ".validate": "newData.isNumber() && newData.val() >= 0" },
"category": { ".validate": "newData.isString()" },
"$other": { ".validate": false }
}
}
}
}"$other": {".validate": false} impede que campos não especificados sejam adicionados, garantindo que apenas os campos definidos existam no documento.{
"rules": {
"adminPanel": {
".read": "auth != null && auth.token.admin == true",
".write": "auth != null && auth.token.admin == true"
},
"moderatorContent": {
".read": "auth != null && (auth.token.moderator == true || auth.token.admin == true)",
".write": "auth != null && (auth.token.moderator == true || auth.token.admin == true)"
},
"userProfiles": {
"$uid": {
".read": "auth != null",
".write": "auth != null && (auth.uid == $uid || auth.token.admin == true)"
}
}
}
}// Tornar usuário admin
admin.auth().setCustomUserClaims(uid, {
admin: true,
moderator: true
});
// Verificar claims
const user = await admin.auth().getUser(uid);
console.log(user.customClaims);{
"rules": {
"comments": {
"$commentId": {
".write": "auth != null &&
root.child('posts').child(newData.child('postId').val()).exists() &&
root.child('users').child(auth.uid).exists()",
".validate": "newData.hasChildren(['postId', 'userId', 'text', 'timestamp']) &&
newData.child('userId').val() == auth.uid &&
newData.child('text').isString() &&
newData.child('text').val().length > 0 &&
newData.child('text').val().length <= 500"
}
},
"userFollowers": {
"$userId": {
"$followerId": {
".write": "auth != null && auth.uid == $followerId",
".validate": "newData.val() == true && root.child('users').child($userId).exists()"
}
}
}
}
}{
"rules": {
"userPosts": {
"$userId": {
"$postId": {
".write": "auth != null &&
auth.uid == $userId &&
(!root.child('userLastPost').child($userId).exists() ||
(now - root.child('userLastPost').child($userId).val()) > 60000)",
".validate": "newData.hasChildren(['content', 'timestamp']) &&
newData.child('timestamp').val() == now"
}
}
},
"userLastPost": {
"$userId": {
".write": "auth != null && auth.uid == $userId",
".validate": "newData.val() == now"
}
}
}
}now do servidor{
"rules": {
"orders": {
"$orderId": {
".write": "auth != null && newData.child('userId').val() == auth.uid",
".validate": "newData.hasChildren(['userId', 'items', 'total', 'status', 'createdAt'])",
"userId": { ".validate": "newData.val() == auth.uid" },
"status": { ".validate": "newData.val() == 'pending'" },
"createdAt": { ".validate": "newData.val() == now" },
"total": { ".validate": "newData.isNumber() && newData.val() > 0" },
"items": {
".validate": "newData.hasChildren()",
"$itemId": {
".validate": "newData.hasChildren(['productId', 'quantity', 'price'])",
"productId": { ".validate": "newData.isString() && root.child('products').child(newData.val()).exists()" },
"quantity": { ".validate": "newData.isNumber() && newData.val() > 0 && newData.val() <= 100" },
"price": { ".validate": "newData.isNumber() && newData.val() > 0" },
"$other": { ".validate": false }
}
},
"$other": { ".validate": false }
}
}
}
}{
"rules": {
"transactions": {
"$txId": {
".write": "auth != null &&
(!data.exists() || auth.token.admin == true)",
".validate": "newData.hasChildren(['userId', 'amount', 'timestamp'])",
"userId": {
".validate": "newData.val() == auth.uid &&
(!data.exists() || newData.val() == data.val())"
},
"amount": {
".validate": "newData.isNumber() &&
(!data.exists() || newData.val() == data.val())"
},
"timestamp": {
".validate": "!data.exists() && newData.val() == now"
}
}
}
}
}!data.exists() || newData.val() == data.val() permite criar mas não modificar o campo.{
"rules": {
"privateGroups": {
"$groupId": {
".read": "auth != null &&
(root.child('groupMembers').child($groupId).child(auth.uid).exists() ||
root.child('groupAdmins').child($groupId).child(auth.uid).exists())",
".write": "auth != null &&
root.child('groupAdmins').child($groupId).child(auth.uid).exists()"
}
},
"groupMembers": {
"$groupId": {
"$userId": {
".write": "auth != null &&
root.child('groupAdmins').child($groupId).child(auth.uid).exists()",
".validate": "newData.val() == true"
}
}
},
"groupAdmins": {
"$groupId": {
"$userId": {
".write": "auth != null &&
root.child('groupAdmins').child($groupId).child(auth.uid).exists()",
".validate": "newData.val() == true"
}
}
}
}
}rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if false;
}
}
}rules_version = '2' é obrigatório e habilita recursos modernos.read - Permite downloads (get, list)write - Permite uploads (create, update, delete)get - Download de arquivo específicolist - Listar arquivos em diretóriocreate - Criar novo arquivoupdate - Modificar arquivo existentedelete - Remover arquivoread e write para simplicidade, ou operações granulares para controle preciso.request.auth - Info do usuário autenticadorequest.auth.uid - ID do usuáriorequest.auth.token - Custom claimsrequest.resource - Arquivo sendo enviadorequest.resource.size - Tamanho em bytesrequest.resource.contentType - MIME typeresource - Arquivo existente (em updates)resource.metadata - Metadados customizadosrules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/avatar.{extension} {
allow read: if true;
allow write: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/.*')
&& extension.matches('(jpg|jpeg|png|gif)');
}
}
}
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/documents/{document} {
allow read: if request.auth != null
&& request.auth.uid == userId;
allow create: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 10 * 1024 * 1024
&& request.resource.contentType in [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'image/jpeg',
'image/png'
];
allow delete: if request.auth != null
&& request.auth.uid == userId;
}
}
}create de delete - isso permite adicionar regras diferentes para cada operação se necessário no futuro.rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /posts/{postId}/media/{mediaFile} {
allow read: if true;
allow create: if request.auth != null
&& request.resource.size < 20 * 1024 * 1024
&& request.resource.contentType.matches('(image|video)/.*')
&& request.resource.metadata.uploadedBy == request.auth.uid
&& request.resource.metadata.postId == postId
&& request.resource.metadata.keys().hasAll(['uploadedBy', 'postId', 'description']);
allow update: if request.auth != null
&& resource.metadata.uploadedBy == request.auth.uid
&& request.resource.metadata.uploadedBy == resource.metadata.uploadedBy
&& request.resource.metadata.postId == resource.metadata.postId;
allow delete: if request.auth != null
&& resource.metadata.uploadedBy == request.auth.uid;
}
}
}storageRef.put(file, {customMetadata: {uploadedBy: uid, postId: 'abc', description: 'Foto da festa'}})rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Área pública - só admins podem modificar
match /public/{file} {
allow read: if true;
allow write: if request.auth != null
&& request.auth.token.admin == true;
}
// Área de moderadores
match /moderation/{file} {
allow read, write: if request.auth != null
&& (request.auth.token.moderator == true
|| request.auth.token.admin == true);
}
// Uploads de usuários verificados
match /verified-uploads/{userId}/{file} {
allow read: if true;
allow write: if request.auth != null
&& request.auth.uid == userId
&& request.auth.token.email_verified == true
&& request.resource.size < 15 * 1024 * 1024;
}
}
}rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /gallery/{imageId} {
allow read: if true;
allow create: if request.auth != null
&& request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/(jpeg|png|webp)')
&& request.resource.metadata.validated != 'true';
allow update: if request.auth != null
&& resource.metadata.uploadedBy == request.auth.uid
&& request.resource.metadata.validated == 'true';
allow delete: if request.auth != null
&& (resource.metadata.uploadedBy == request.auth.uid
|| request.auth.token.admin == true);
}
}
}const functions = require('firebase-functions');
const admin = require('firebase-admin');
const sharp = require('sharp');
exports.validateImage = functions.storage.object().onFinalize(async (object) => {
const filePath = object.name;
if (!filePath.startsWith('gallery/')) return;
const bucket = admin.storage().bucket();
const file = bucket.file(filePath);
const [buffer] = await file.download();
const metadata = await sharp(buffer).metadata();
if (metadata.width < 800 || metadata.height < 600) {
await file.delete();
return;
}
await file.setMetadata({
metadata: { validated: 'true' }
});
});rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Organizações > Projetos > Arquivos
match /organizations/{orgId}/projects/{projectId}/{allPaths=**} {
// Ler se for membro da organização
allow read: if request.auth != null
&& firestore.get(/databases/(default)/documents/organizations/$(orgId)/members/$(request.auth.uid)).data.role != null;
// Escrever se for admin ou manager do projeto
allow write: if request.auth != null
&& (firestore.get(/databases/(default)/documents/organizations/$(orgId)/members/$(request.auth.uid)).data.role in ['admin', 'owner']
|| firestore.get(/databases/(default)/documents/organizations/$(orgId)/projects/$(projectId)/members/$(request.auth.uid)).data.role == 'manager');
}
// Pasta compartilhada da organização
match /organizations/{orgId}/shared/{file} {
allow read: if request.auth != null
&& firestore.exists(/databases/(default)/documents/organizations/$(orgId)/members/$(request.auth.uid));
allow create: if request.auth != null
&& firestore.exists(/databases/(default)/documents/organizations/$(orgId)/members/$(request.auth.uid))
&& request.resource.size < 50 * 1024 * 1024;
}
}
}rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Funções auxiliares
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
function isAdmin() {
return isAuthenticated() && request.auth.token.admin == true;
}
function isValidImage() {
return request.resource.contentType.matches('image/.*')
&& request.resource.size < 5 * 1024 * 1024;
}
function isValidDocument() {
return request.resource.contentType in [
'application/pdf',
'application/msword',
'text/plain'
]
&& request.resource.size < 10 * 1024 * 1024;
} // Usando as funções
match /users/{userId}/avatar {
allow read: if true;
allow write: if isOwner(userId) && isValidImage();
}
match /users/{userId}/documents/{doc} {
allow read: if isOwner(userId) || isAdmin();
allow write: if isOwner(userId) && isValidDocument();
}
match /admin/{file} {
allow read, write: if isAdmin();
}
}
}.read: true e .write: true globalmente em produção. Sempre comece negando tudo..validate no Database e validações de contentType/size no Storage para garantir integridade.auth != null em praticamente todas as regras de escrita.