私の人生では、書き込みを許可するために次の結果がfalse
になる理由を理解できません。 users
コレクションを開始するには空であると仮定し、次の形式のドキュメントをAngularフロントエンドから作成しています:
_{
displayName: 'FooBar',
email: '[email protected]'
}
_
私の現在のセキュリティルール:
_service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function isAdmin() {
return resource.data.role == 'ADMIN';
}
function isEditingRole() {
return request.resource.data.role != null;
}
function isEditingOwnRole() {
return isOwnDocument() && isEditingRole();
}
function isOwnDocument() {
return request.auth.uid == userId;
}
allow read: if isOwnDocument() || isAdmin();
allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
}
}
}
_
一般的に、ユーザーが自分の役割を編集できないようにします。それ以外の場合、通常のユーザーは自分のドキュメントを編集でき、管理者は誰でも編集できます。
false
にisEditingRole()
をスタブすると、期待される結果が得られるため、その式に絞り込みました。
書き込みが誤って戻ってくるので、理由を判断できません。どんなアイデアや修正も役立つでしょう!
編集1
私が試したこと:
_function isEditingRole() {
return request.resource.data.keys().hasAny(['role']);
}
_
そして
_function isEditingRole() {
return 'role' in request.resource.data;
}
_
そして
_function isEditingRole() {
return 'role' in request.resource.data.keys();
}
_
Edit 2
最終的に、管理者はユーザーのロールを設定するため、最終的にドキュメントにロールが存在することに注意してください。つまり、以下の Firestore docs によれば、元のリクエストに含まれていなくても、リクエストにはrole
キーが含まれます。
リソースに存在するリクエストで提供されていないフィールドは、_
request.resource.data
_に追加されます。ルールは、_request.resource.data.foo
_を_resource.data.foo
_と比較することで、フィールドが変更されているかどうかをテストできます。これは、書き込み要求で送信されなかった場合でも、resource
のすべてのフィールドが_request.resource
_にも存在することを知っています。
それによると、「編集1」の3つのオプションは除外されていると思います。私は_request.resource.data.role != resource.data.role
_の提案を試みましたが、それも機能していません...私は迷っていて、実際にFirestoreにバグがあるのではないかと思い始めています。
結局、実際にfalse
を返すときに_resource.data.nonExistentField == null
_がError
を返すと仮定していたようです( this と私のテストによると) )。だから、私の元の解決策はそれに走っていたかもしれません。 docs に従って反対が機能するはずであるため、これは不可解です。しかし、ドキュメントは、キーではなく「存在しない」値を参照している可能性があります-微妙な違い。
私はまだ100%の明確さを持っていませんが、これは私がうまくいったことになりました:
_function isAddingRole() {
return !('role' in resource.data) && 'role' in request.resource.data;
}
function isChangingRole() {
return 'role' in resource.data && 'role' in request.resource.data && resource.data.role != request.resource.data.role;
}
function isEditingRole() {
return isAddingRole() || isChangingRole();
}
_
依然として私を困惑させているのは、ドキュメントによると、isChangingRole()
の_&& 'role' in request.resource.data
_部分は必要ないということです。Firestoreによって自動的に挿入されるためです。これを削除すると、権限の問題で書き込みが失敗するため、そうではありませんでした。
書き込みをallow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
だけでなく、create
、update
、およびdelete
の部分に分割することで、明確化/改善される可能性があります。
更新を確認するためのカスタム関数を作成すると、ルールがはるかに読みやすく、保守しやすくなります。例えば:
service cloud.firestore {
match /databases/{database}/documents {
function isUpdatingField(fieldName) {
return (!fieldName in resource.data && fieldName in request.resource.data) || resource.data[fieldName] != request.resource.data[fieldName];
}
match /users/{userId} {
//read rules here...
allow write: if !isUpdatingField("role") && !isUpdatingField("adminOnlyAttribute");
}
}
}
writeFields
を使用して解決しました。このルールを試してください。
allow write: if !('role' in request.writeFields);
私の場合、list
を使用してフィールドの更新を制限しています。それも機能します。
allow update: if !(['leader', '_created'] in request.writeFields);
ドキュメント内のwriteFieldsへの参照がなくなったため、writeFieldsでできることを行うための新しい方法を考え出す必要がありました。
function isSameProperty(request, resource, key) {
return request.resource.data[key] == resource.data[key]
}
match /myCollection/{id} {
// before version !request.writeFields.hasAny(['property1','property2','property3', 'property4']);
allow update: isSameProperty(request, resource, 'property1')
&& isSameProperty(request, resource, 'property2')
&& isSameProperty(request, resource, 'property3')
&& isSameProperty(request, resource, 'property4')
}
この単一の機能を使用すると、フィールドが作成/変更されているかどうかを確認できます。
function incomingDataHasFields(fields) {
return ((
request.writeFields == null
&& request.resource.data.keys().hasAll(fields)
) || (
request.writeFields != null
&& request.writeFields.hasAll(fields)
));
}
使用法:
match /xxx/{xxx} {
allow create:
if incomingDataHasFields(['foo']) // allow creating a document that contains 'foo' field
&& !incomingDataHasFields(['bar', 'baz']); // but don't allow 'bar' and 'baz' fields to be created
これは過剰な殺しのように思えるかもしれませんが、ユーザーが生成していない他のフィールドがある可能性のあるドキュメントを更新する場合などです。作成された役割などには、それらのフィールドが変更されないことをテストできる関数が必要です。したがって、これら3つのFNを満たします。
function hasOnlyFields(fields) {
if request.resource.data.keys().hasOnly(fields)
}
function hasNotChanged(fields) {
return (fields.size() < 1 || equals(fields[0]))
&& (fields.size() < 2 || equals(fields[1]))
&& (fields.size() < 3 || equals(fields[2]))
&& (fields.size() < 4 || equals(fields[3]))
&& (fields.size() < 5 || equals(fields[4]))
&& (fields.size() < 6 || equals(fields[5]))
&& (fields.size() < 7 || equals(fields[6]))
&& (fields.size() < 8 || equals(fields[7]))
&& (fields.size() < 9 || equals(fields[8]))
}
function equals(field) {
return field in request.resource.data && field in resource.data && request.resource.data[field] == request.resource.data[field]
}
そのため、ユーザーは名前、年齢、住所のみを更新できますが、ロールとメールは更新できません。
allow update: if hasOnlyFields(['name', 'age', 'address']) && hasNotChanged(['email', 'roles'])
HasNotChangedは最大9つのフィールドをチェックできます。また、実行したいチェックはこれらだけではありません。ドキュメントのタイプと所有権も確認する必要があります。
クライアントでreadonlyであるフィールドを強制するには、writeFields
(- https://firebase.google.com/docs/reference/ rules/rules.firestore.Request#writeFields )。 gekijinはこれを提案しましたが、構文が少し間違っています。
match /myCollection/{id}
// Readonly fields
allow update: if !(request.writeFields.hasAny(['field1', 'field2']));
}
FirestoreがwriteFieldsを生成するため、上記のチェックはupdate
およびcreate
ルールで安全に実行できます。
2018年10月9日編集
@ dls101は、GoogleがwriteFields
の言及をドキュメントから削除したように思われることを知らせるのに十分親切でした。したがって、このソリューションの使用には注意してください。
トム・ベイリーの解決策( https://stackoverflow.com/a/48177722/5727205 )は有望に見えました。
しかし、私の場合は、フィールドが編集されないようにする必要があり、既存のデータにフィールドが存在しないというケースがあります。それにより、フィールドが存在するかどうかのチェックを追加しました。
このソリューションは2つのチェックをチェックします。
function isNotUpdatingField(fieldName) {
return
( !(fieldName in request.resource.data) && !(fieldName in resource.data) ) ||
request.resource.data[fieldName] == resource.data[fieldName];
}