ChatGPTを使って、Firestoreのセキュリティールールを作ってみた
こんにちは、フットボール・テクノロジーズ社でフロントエンジニアとして働いているpeko-pekoです。Zennでの2本目の投稿になります😆
今回は、業務委託で開発しているサービスの中で、FirestoreのセキュリティールールをChatGPTと一緒に会話しながら作ったので、整理も兼ねて記事にしたいと思います💪
tl;dr
- Firestoreのセキュリティールールを、ChatGPTと一緒に会話しながら作ってみた
- 完成度としては80%程度。20%は手動で修正が必要
- ChatGPTの出力する文字数制限があるので、コピペが大変😭
やったこと
Firebaseのfirestoreを利用する際に大事なのがセキュリティールールをガッチリ作ること。
過去には自力で作っていたが、ChatGPTを使ってセキュリティールールを自然言語で書けないか?という疑問から、下記のフローでルールとテストを作ってみた。
ChatGPTとの会話のフロー
- ユーザーストーリーを自然言語で書く。サンプルコードを少し書く。
- 1をChatGPTに投げて、セキュリティールールを作ってもらう
- 2の完成度が90%程度なので、手動で修正
- 3をChatGPTに投げて、セキュリティールールのテストを作ってもらう
- 4の完成度が70%程度なので、手動で修正
- 5のテストを実行して、全てがPassすることを確認
最初にChatGPTに投げたprompt
実際に作成したセキュリティーコードは、collectionの数だけあるので膨大な行数になりました。
ここでは、例として一般的な下記のコレクションを想定したコードを共有します。
説明用のサンプルDB
コレクション名
users: /users/{userId}
{
displayName: "peko-peko",
icon: "https://xxxxx/peko-peko.jpg"
}
=> 全ての人が閲覧できる
-------
コレクション名
secrets: /users/{userId}/secrets/{secretId}
{
email: "peko-peko@gmail.com"
}
=> Adminと本人のみが、閲覧できる
上記のようにUserコレクションとサブコレクションであるSecretコレクションが存在するとします。Userは誰でも閲覧可能ですが、Secretsは本人とAdmin管理者のみが閲覧できます。
これらのセキュリティールールを作るときに、下記のpromptをChatGPTに投げます。
ポイントは、サンプルコードを一緒に投げることで、想定したoutputの形になりやすいです。ただし、inputの文字数制限があるので、その辺りの調整が必要。
firebaseのfirestoreのセキュリティルールを作ってください
下記の要件を満たすようにしてください
### 要件
- パス:/users/{userId}
- read
- ログインしていない場合
- user が表示できる
- ログインしている場合
- user が表示できる
- create
- ログインしていない場合
- user が作成できない
- ログインしている場合
- admin の場合
- user が作成できる
- admin ではない場合
- user が作成できない
- update
- ログインしていない場合
- user が更新できない
- ログインしている場合
- admin の場合
- user が更新できる
- admin ではない場合
- userId === request.auth.token.userId の場合
- user が更新できる
- userId !== request.auth.token.userId の場合
- user が更新できない
- パス:/users/{userId}/secrets/{secretId}
- read
- ログインしていない場合
- secret が表示できない
- ログインしている場合
- admin の場合
- secret が表示できる
- admin ではない場合
- secretId === request.auth.token.userId の場合
- secret が表示できる
- secretId !== request.auth.token.userId の場合
- secret が表示できない
- create
- ログインしていない場合
- secret が作成できない
- ログインしている場合
- admin の場合
- secret が作成できる
- admin ではない場合
- secret が作成できない
- update
- ログインしていない場合
- secret が更新できない
- ログインしている場合
- admin の場合
- secret が更新できる
- admin ではない場合
- secretId === request.auth.token.userId の場合
- secret が更新できる
- secretId !== request.auth.token.userId の場合
- secret が更新できない
### サンプルコード
function isAuthenticated() {
return request.auth != null;
}
function isAdmin() {
return isAuthenticated() && request.auth.token.userType == 1;
}
match /users/{userId} {
allow read;
allow create: if isAdmin();
allow update: if isAdmin() || request.auth.token.userId == userId;
}
セキュリティールールのテストを作成するprompt
上記で生成されたセキュリティールールのcode全てをpromptに投げます。
ポイントは、ここでもサンプルコードを追加で投げることで、思ったようなoutputを得られる確率が上がります。
Firestoreのセキュリティールールがあります。
このテストを作成してください。
### セキュリティールール
ここに1で作成したルールを貼り付ける
### サンプルコード
describe("users/{userId}", () => {
describe("READ", () => {
test("全員 が読み込み可能", async () => {
await initDB("users/sato", { displayName: "佐藤", userType: 3 });
const db = await logoutAuth();
const ref = doc(db, "users/sato");
await assertSucceeds(getDoc(ref));
});
});
describe("CREATE", () => {
test("Admin のみが作成可能", async () => {
const db = await loginAuth({
uid: "admin",
userType: 1
});
const ref = doc(db, "users/user1");
await assertSucceeds(setDoc(ref, { displayName: "佐藤" }));
});
test("未ログイン は作成不可", async () => {
const db = await logoutAuth();
const ref = doc(db, "users/user1");
await assertFails(setDoc(ref, { displayName: "佐藤" }));
});
test("seller は作成不可", async () => {
const db = await loginAuth({
uid: "seller",
userId: "user1",
userType: 2
});
const ref = doc(db, "users/user1");
await assertFails(setDoc(ref, { displayName: "佐藤" }));
});
test("buyer は作成不可", async () => {
const db = await loginAuth({
uid: "buyer",
userId: "user1",
userType: 3
});
const ref = doc(db, "users/user1");
await assertFails(setDoc(ref, { displayName: "佐藤" }));
});
});
});
難しかった点
冒頭にも書いた通り、想定したoutputの80%程度の完成度になりました。特に、firestoreのDB構造(サブコレクション)などの下記を、ChatGPTに理解してもらうのが難しかった。
- CollectionGroupで取得するコレクションのObject構造を理解させる
- pathからのuserIdと、DBからのresource.data.user.id を区別させる。特にCollectionGroupの場合!
- custom claims の値を使う
- 大量のrulesを作ってもらうと、しれっと平気で嘘を書くことがある
- 同じpromptを投げても、毎回違う結果になるので、PDCAが回しづらい(継続的な少しづつの修正がしづらい)
今後の運用方法
運用していると、collectionが増えたり、rulesの中身を修正することがあります。
そんな時は、既に完成したrules、testをChatGPTに投げて理解させた上で、追加 or 修正の差分をChatGPTに投げることでoutputの精度が上がることが分かりました。
具体的には、、、
- 既存のrulesをChatGPTに投げる
- 既存のtestをChatGPTに投げる
- 新たに追加したい差分のみを投げて、outputを依頼する
3の例。
既存のセキュリティールールに、下記のcollectionを追加したいです。
セキュリティールールとテストを書いてください。
### 追加
- パス:/users/{userId}/likes/${likeId}
- read
- ログインしていない場合
- like が表示できる
- ログインしている場合
- like が表示できる
...
最後に。。。
今回、ChatGPTにセキュリティールールを作ってもらった。promptの調整次第でさらに精度が上がると感じたが、同時に、対象コレクションが増えると、どうしても文字数制限に引っかかる問題が発生する。コレクションを分割して投げるなどの工夫が必要であり、手動でのコピペの回数が増えるのは悩ましいところ。
今後は、APIを利用して機械的にテキストを分割して、最後に集約するような形に調整したいと思います💪
最後に、株式会社フットボール・テクノロジーズでは、Firebaseを利用したサービスを業務委託として数多く開発しています。興味がある方がいましたら、下記よりコンタクトをお願いします🙇♂️
今日もお腹がペコペコ。
Discussion