我的用户个人资料数据存储在Firestore中。我在Firestore中也有一些配置文件字段,这些字段决定了用户权限级别。如果用户包含任何会影响其权限级别的更改,我想拒绝用户对其Firestore个人资料进行写入或更新的功能。
用户的Firestore文档中用于其个人资料的示例字段
permissionLevel: 1
favoriteColor: red
文档ID与用户的身份验证uid相同(因为只有用户才可以读取/写入/更新其个人资料)。
如果用户的Firestore更新或写入包含PermissionLevel字段,我想拒绝更新或写入,以防止用户更改自己的权限级别。
当前的Firestore规则
当我在模拟器中构建对象以测试包括或不包括称为“ permissionLevel”的字段时,此方法工作正常。但这拒绝了来自客户端Web SDK的所有更新/写入请求。
service cloud.firestore {
match /databases/{database}/documents {
// Deny all access by default
match /{document=**} {
allow read, write: if false;
}
// Allow users to read, write, and update their own profiles only
match /users/{userId} {
// Allow users to read their own profile
allow read: if request.auth.uid == userId;
// Allow users to write / update their own profile as long as no "permissionLevel" field is trying to be added or updated
allow write, update: if request.auth.uid == userId &&
request.resource.data.keys().hasAny(["permissionLevel"]) == false;
}
}
}
客户端功能
例如,此功能尝试通过更新Firestore字段来仅在用户上次活动时进行更新。这将返回错误Error updating user refresh time: Error: Missing or insufficient permissions.
/**
* Update User Last Active
* Updates the user's firestore profile with their "last active" time
* @param {object} user is the user object from the login auth state
* @returns {*} dispatched action
*/
export const updateLastActive = (user) => {
return (dispatch) => {
firestore().settings({/* your settings... */ timestampsInSnapshots: true});
// Initialize Cloud Firestore through Firebase
var db = firestore();
// Get the user's ID from the user retrieved user object
var uid = firebaseAuth().currentUser["uid"];
// Get last activity time (last time token issued to user)
user.getIdTokenResult()
.then(res => {
let lastActive = res["issuedAtTime"];
// Get reference to this user's profile in firestore
var userRef = db.collection("users").doc(uid);
// Make the firestore update of the user's profile
console.log("Firestore write (updating last active)");
return userRef.update({
"lastActive": lastActive
})
})
.then(() => {
// console.log("User lastActive time successfully updated.");
})
.catch(err => {
console.log("Error updating user refresh time: ", err);
})
}
}
如果我从Firestore规则中删除此行,则此功能也可以正常工作。我看不到他们之间有什么关系,以及为什么它在模拟器中可以正常工作。
request.resource.data.keys().hasAny(["permissionLevel"]) == false;
答案 0 :(得分:2)
好的,我找到了一个解决方案,但我在firebase文档中找不到任何官方文档来支持此方法。它在模拟中不起作用,但可以使用IRL。
替换(来自上面的示例)
request.resource.data.keys().hasAny(["permissionLevel"]) == false
此操作
!("permissionLevel" in request.writeFields);
完整工作权限示例
service cloud.firestore {
match /databases/{database}/documents {
// Deny all access by default
match /{document=**} {
allow read, write: if false;
}
// Allow users to read, write, and update their own profiles only
match /users/{userId} {
// Allow users to read their own profile
allow read: if request.auth.uid == userId;
// Allow users to write / update their own profile as long as no "admin" field is trying to be added or created
allow write, update: if request.auth.uid == userId &&
!("permissionLevel" in request.writeFields);
}
}
}
只要密钥permissionLevel
存在于Firestore请求映射对象中,此操作就可以成功地阻止更新或写入操作,并允许其他预期的更新。
文档帮助
Firestore Security Docs Index列出了“ rules.firestore.Request#writeFields”-但是,单击它时,结果页面甚至根本没有提到“ writeFields”。
我将基于rules.Map的原则用于
x中的k 检查密钥x是否存在于地图x
中答案 1 :(得分:0)
您可以考虑添加权限级别的另外两件事:
使用具有自定义声明的Firebase身份验证令牌。奖励:此方法不会触发对数据库的读取。我建议您查看以下Firecast:
Add the Firebase Admin SDK to Your Server指南也很有帮助。
我是开发游戏的新手,但这是我使用ItelliJ IDEA手动创建自定义声明的方法:
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.UserRecord;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class App {
//This will be the UID of the user we modify
private static final String UID = "[uid of the user you want to modify]";
//Different Roles
private static final int USER_ROLE_VALUE_BASIC = 0; //Lowest Level - new user
private static final int USER_ROLE_VALUE_COMMENTER = 1;
private static final int USER_ROLE_VALUE_EDITOR = 2;
private static final int USER_ROLE_VALUE_MODERATOR = 3;
private static final int USER_ROLE_VALUE_SUPERIOR = 4;
private static final int USER_ROLE_VALUE_ADMIN = 9;
private static final String FIELD_ROLE = "role";
//Used to Toggle Different Tasks - Currently only one task
private static final boolean SET_PRIVILEGES = true; //true to set User Role
//The privilege level being assigned to the uid.
private static final int SET_PRIVILEGES_USER_ROLE = USER_ROLE_VALUE_BASIC;
public static void main(String[] args) throws IOException {
// See https://firebase.google.com/docs/admin/setup for setting this up
FileInputStream serviceAccount = new FileInputStream("./ServiceAccountKey.json");
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl("https://[YOUR_DATABASE_NAME].firebaseio.com")
.build();
FirebaseApp.initializeApp(options);
// Set privilege on the user corresponding to uid.
if (SET_PRIVILEGES){
Map<String, Object> claims = new HashMap<>();
claims.put(FIELD_ROLE, SET_PRIVILEGES_USER_ROLE);
try{
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
FirebaseAuth.getInstance().setCustomUserClaims(UID, claims);
// Lookup the user associated with the specified uid.
UserRecord user = FirebaseAuth.getInstance().getUser(
System.out.println(user.getCustomClaims().get(FIELD_ROLE));
}catch (FirebaseAuthException e){
System.out.println("FirebaseAuthException caught: " + e);
}
}
}
}
build.gradle依赖项当前为:
implementation 'com.google.firebase:firebase-admin:6.7.0'