I have created wild Firebase rules and would like to add another check to them. I want to check two specific fields that can only be changed by the user if they are before the deadline.
The deadline timestamp is stored in the Firebase database as a "number" in seconds (e.g.: 1708074000):
/databases/$(database)/documents/app/config.deadline
.The two fields to be checked are
I have started with the following function, but I can't get any further:
// Check if action is before deadline
function isBeforeDeadline() {
let deadlineTimestamp = get(/databases/$(database)/documents/app/config).deadline;
let isBeforeDeadline = deadlineTimestamp > request.time;
return isBeforeDeadline
}
Here is my entire Firebase rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Check if "role" is not present in the request
function isRoleNotPresent() {
return !('role' in request.resource.data);
}
// Check if "role" is valid for normal users
// Normal users can only have "guest" as their role
function isRoleValueValid() {
return request.resource.data.role == 'guest'
|| request.resource.data.role == 'invited'
|| request.resource.data.role == 'declined';
}
// Check if "role" is not changed
// Normal users can't change their role
function isRoleNotChanged() {
let role = get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
return request.resource.data.role == role;
}
// Check if "role" is "guest"
// Normal users can have "guest" as their role
function isRoleGuest() {
return request.resource.data.role == 'guest';
}
// Check if user is "admin"
function isAdmin() {
let role = get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
return role == 'admin'
}
// Allow the user to access documents in the "users" collection
match /users/{userId} {
// User can read their own profile
allow read: if request.auth.uid == userId;
// User can delete their own profile
allow delete: if request.auth != null && request.auth.uid == userId;
// User can create their own profile
// But cannot set a different "role" (must be "guest"):
// - Checks if no role field in request.resource.data
// - Or checks if role is not modified
// - Or checks if role is "guest"
// - And checks everytime if role is a valid value
// Users with "role" equal to "admin" can create any user's profile
allow create: if (request.auth.uid == userId
&& (
isRoleNotPresent()
|| isRoleNotChanged()
|| isRoleGuest()
)
&& isRoleValueValid()
)
|| isAdmin();
// User can only update their own profile
// But cannot set their "role":
// - Checks if no role field in request.resource.data
// - Or checks if role is not modified
// - And checks everytime if role is a valid value
// Users with "role" equal to "admin" can edit any user's profile
allow update: if (request.auth.uid == userId
&& (isRoleNotPresent() || isRoleNotChanged())
&& isRoleValueValid()
)
|| isAdmin();
}
// Allow access to the "config" document
match /app/config {
allow read: if true; // Everyone can read
// Allow write only if user role from /users/{userId} is "admin"
allow write: if isAdmin();
}
}
}
I separated each field to three condition, Then write rule for each one and added together.
invitation
field not exists and after update still not exists.invitation
field is same value after update.invitation
field is updated to different value and must before deadline.The reason why 1 and 2 condition exists is if you are trying to update other field and number 3 condition could restrict that update.
function checkInvitationField() {
return
// 1 is not exists
(!('invitation' in request.resource.data) && !('invitation' in resource.data)) ||
// 2 is same
(request.resource.data.invitation == resource.data.invitation) ||
// 3 update before deadline
(request.resource.data.invitation != resource.data.invitation && isBeforeDeadline());
}
Put it to profile path:
// User can only update their own profile
// But cannot set their "role":
// - Checks if no role field in request.resource.data
// - Or checks if role is not modified
// - And checks everytime if role is a valid value
// Users with "role" equal to "admin" can edit any user's profile
allow update: if (request.auth.uid == userId
&& (isRoleNotPresent() || isRoleNotChanged())
&& isRoleValueValid()
// I add it here, If needed add to create depend on your case.
&& checkInvitationField()
)
|| isAdmin();
I did some similar logic in my project, But I haven't test code above yet, So if you got any problem you can comment below, I will edit my answer.