• トップ
  • ブログ一覧
  • テスト駆動で学ぶ Firestore セキュリティルール【カスタム関数編:第4回】
  • テスト駆動で学ぶ Firestore セキュリティルール【カスタム関数編:第4回】

    広告メディア事業部広告メディア事業部
    2020.06.10

    IT技術

    【第4回】テスト駆動で成績データコレクションのルールを実装する

    前回の記事では、成績コレクション records のデータ更新ルールを実装しつつ、ルールの見通しをよくするため、カスタム関数を使ってルールを整理してみました。

    今回は、成績コレクション records のデータ取得・削除用ルールの実装を進めていきます。

    データ取得・削除用ルールの作成後、最後に、これまでに作成した全てのテストを実行して、作成したセキュリティルールが第1回に設定した要件に沿ったルールとなっているかを確認します。

    前回の記事はこちら

    前回の記事は、以下をご参照ください。

    test-dirven-firestore-security-rules-custom-function-3-organizing-rules2020.06.09テスト駆動で学ぶ Firestore セキュリティルール 【カスタム関数編:第3回】【第3回】テスト駆動で成績データコレクションのルールを実装する前回の記事では、成績コレクション records のデー...

    Firestore 上のデータ取得ルールの実装について

    テストの作成

    ここまでの実装と同様に、まずは、データ取得の要件を再確認します。

    1. admin ユーザはデータの取得可

    teacher ユーザの取得要件

    1. 担任でないデータは取得不可

    student ユーザの取得要件

    1. 自分以外のデータは取得不可
    2. 対象シーズンが成績評価期間の場合は取得不可

    上記の要件に沿って、テストを作成します。

    tests/data.read.test.ts ファイルを追加して、以下のようにコードを記述してください。

    テストのコード

    1process.env.FIRESTORE_EMULATOR_HOST = "localhost:58080";
    2
    3import { FirestoreTestSupporter } from "firestore-test-supporter";
    4import * as path from "path";
    5import * as firebase from "@firebase/testing";
    6import { users } from "./data/collections/users";
    7import { records } from "./data/collections/records";
    8import { seasons } from "./data/collections/seasons";
    9import InitialData from "./data/InitialData";
    10
    11describe("成績データの取得テスト", () => {
    12    const rulesFilePath = path.join(__dirname, "firestore.rules");
    13    const supporter = new FirestoreTestSupporter("my-test-project", rulesFilePath);
    14
    15    beforeEach(async () => {
    16        await supporter.loadRules();
    17
    18        // 各コレクションに初期データを追加
    19        const initialData = new InitialData(rulesFilePath);
    20        await initialData.setupUsers();
    21        await initialData.setupRecords();
    22        await initialData.setupSeasons();
    23    });
    24
    25    afterEach(async () => {
    26        await supporter.cleanup()
    27    });
    28
    29    test("adminユーザはデータの取得可", async () => {
    30        // adminユーザで認証されたクライアントを取得
    31        const db = supporter.getFirestoreWithAuth(users.sample.admin);
    32
    33        const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    34        await firebase.assertSucceeds(doc.get());
    35    });
    36
    37    test("ログインしていないユーザは取得不可", async () => {
    38        // 認証されていないクライアントを取得
    39        const db = supporter.getFirestore();
    40
    41        const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    42        await firebase.assertFails(doc.get());
    43    });
    44
    45    describe("teacherユーザの取得要件", () => {
    46        let doc: firebase.firestore.DocumentReference;
    47        beforeEach(() => {
    48            // teacherユーザで認証されたクライアントを取得
    49            const db = supporter.getFirestoreWithAuth(users.sample.teacher);
    50            doc = db.collection(records.collectionPath).doc(records.initialData.id);
    51        });
    52
    53        test('要件にあったデータの取得に成功', async () => {
    54            await firebase.assertSucceeds(doc.get());
    55        });
    56
    57        test("担任でないデータは取得不可", async () => {
    58            // データベース上のデータの担任を変更
    59            const dbWithAdmin = supporter.getAdminFirestore();
    60            const docWithAdmin = dbWithAdmin.collection(records.collectionPath).doc(records.initialData.id);
    61            const newData = { ...records.validUpdateDataForTeacher, homeroomTeacher: "other_teacher" };
    62            await firebase.assertSucceeds(docWithAdmin.update(newData));
    63
    64            await firebase.assertFails(doc.get());
    65        })
    66    });
    67
    68    describe("studentユーザの取得要件", () => {
    69        test('要件にあったデータの取得に成功', async () => {
    70            // データベース上のデータの成績評価期間フラグをfalseに変更
    71            const dbWithAdmin = supporter.getAdminFirestore();
    72            const seasonDoc = dbWithAdmin.collection(seasons.collectionPath).doc(seasons.initialData(true).id);
    73            await firebase.assertSucceeds(seasonDoc.update(seasons.initialData(false)));
    74
    75            // studentユーザで認証されたクライアントを取得
    76            const db = supporter.getFirestoreWithAuth(users.sample.student);
    77
    78            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    79            await firebase.assertSucceeds(doc.get());
    80        });
    81
    82        test("自分以外のデータは取得不可", async () => {
    83            // データベース上のデータの成績評価期間フラグをfalseに変更
    84            const dbWithAdmin = supporter.getAdminFirestore();
    85            const seasonDoc = dbWithAdmin.collection(seasons.collectionPath).doc(seasons.initialData(true).id);
    86            await firebase.assertSucceeds(seasonDoc.update(seasons.initialData(false)));
    87
    88            // 成績データの生徒と異なる生徒で認証されたクライアントを取得
    89            const db = supporter.getFirestoreWithAuth(users.sample.other_student);
    90
    91            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    92            await firebase.assertFails(doc.get());
    93        });
    94
    95        test("対象シーズンが成績評価期間の場合は取得不可", async () => {
    96            // studentユーザで認証されたクライアントを取得
    97            const db = supporter.getFirestoreWithAuth(users.sample.student);
    98
    99            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    100            await firebase.assertFails(doc.get());
    101        })
    102    })
    103});

    テストの概要

    各テストで共通の内容

    テストごとに、テスト対象となるロールユーザで認証されたクライアントを取得しています。

    teacher ユーザの取得要件のテスト

    担任でないデータは取得不可 となるテストでは、データ取得前に、データベース上のデータの担任フィールドを、リクエストデータの担任とは異なる担任に変更

    student ユーザの取得要件のテスト

    要件にあったデータの取得に成功 となるテストと、自分以外のデータは取得不可 となるテストでは、データベース上のデータの成績評価期間フラグを「false」に変更して、成績評価期間外に設定しています。

    Firestore 上のデータ取得ルールを実装してみる

    それでは、作成したテストに沿って、セキュリティルールを実装していきましょう。

    全ての read アクセスを許可するルールを設定

    データ更新ルールの実装のときと同じように、今回もまず、データ取得に関するルールだけを実装し、最後に、作成した全てのルールと統合したいと思います。

    まずは、records コレクションの read アクセスを全て許可し、「要件にあったデータの追加に成功」するテストだけを通します

    以下のように、セキュリティルールを調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      // 全てのreadアクセスを許可
    6      allow read: if true;
    7    }
    8  }
    9}

    テストをスタート

    以下のコマンドを実行して、データ取得ルール用のテストをスタートしてください。

    1npm run test-watch tests/data.read.test.ts

    テスト結果

    1 FAIL  tests/data.read.test.ts (5.181s)
    2  成績データの取得テスト
    3adminユーザはデータの取得可 (2150ms)
    4    × ログインしていないユーザは取得不可 (145ms)
    5    teacherユーザの取得要件
    6要件にあったデータの取得に成功 (86ms)
    7      × 担任でないデータは取得不可 (109ms)
    8    studentユーザの取得要件
    9要件にあったデータの取得に成功 (109ms)
    10      × 自分以外のデータは取得不可 (100ms)
    11      × 対象シーズンが成績評価期間の場合は取得不可 (74ms)

    adminユーザはデータの取得可 となるテストと、teacher ユーザ、student ユーザの 要件にあったデータの取得に成功 するテストが成功しました。

    これまで同様に、失敗したテストを順次、通していきます。

    「ログインしていないユーザは取得不可」となるルールを設定

    ログインしていないユーザは取得不可 となるよう、以下のようにルールを調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      allow read: if isLoggedIn();
    10    }
    11  }
    12}

    テスト結果

    1 FAIL  tests/data.read.test.ts
    2  成績データの取得テスト
    3adminユーザはデータの取得可 (97ms)
    4ログインしていないユーザは取得不可 (117ms)
    5    teacherユーザの取得要件
    6要件にあったデータの取得に成功 (1174ms)
    7      × 担任でないデータは取得不可 (134ms)
    8    studentユーザの取得要件
    9要件にあったデータの取得に成功 (115ms)
    10      × 自分以外のデータは取得不可 (104ms)
    11      × 対象シーズンが成績評価期間の場合は取得不可 (82ms)

    ログインしていないユーザは取得不可 となるテストが通りました。

    認証チェックをロールごとに分割

    ここで、admin、teacher、student ユーザ、それぞれのアクセス許可条件を個別に設定するため、認証チェックをロールごとに分割します。

    以下のように、セキュリティルールを調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      function isUserRole(role){
    10        return isLoggedIn()
    11                && role in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
    12      }
    13
    14      function isAdmin(){
    15        return isUserRole("admin");
    16      }
    17
    18      function isTeacher(){
    19        return isUserRole("teacher");
    20      }
    21      
    22      // studentロールチェック
    23      function isStudent(){
    24        return isUserRole("student");
    25      }
    26
    27      allow read: if isAdmin();
    28
    29      allow read: if isTeacher();
    30      
    31      allow read: if isStudent();
    32    }
    33  }
    34}

    データ追加・更新ルールの実装で定義した isAdmin()isTeacher() 関数と同様、student ロールのチェック関数 isStudent() を定義しています。

    ここまでに定義した、ロールチェック用のカスタム関数を使って、認証チェックをロールごとに分割しています。

    ルールの調整による影響がないか確認するため、テスト結果を確認しておきます。

    テスト結果

    1 FAIL  tests/data.read.test.ts
    2  成績データの取得テスト
    3adminユーザはデータの取得可 (1203ms)
    4ログインしていないユーザは取得不可 (111ms)
    5    teacherユーザの取得要件
    6要件にあったデータの取得に成功 (75ms)
    7      × 担任でないデータは取得不可 (104ms)
    8    studentユーザの取得要件
    9要件にあったデータの取得に成功 (92ms)
    10      × 自分以外のデータは取得不可 (92ms)
    11      × 対象シーズンが成績評価期間の場合は取得不可 (62ms)

    テスト結果に変化はないので、続いて teacherユーザの取得要件 テストを通していきます。

    「担任でないデータは取得不可」となるルールを設定

    担任でないデータは取得不可 となるように、セキュリティルールを以下のように調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      function isUserRole(role){
    10        return isLoggedIn()
    11                && role in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
    12      }
    13
    14      function isAdmin(){
    15        return isUserRole("admin");
    16      }
    17
    18      function isTeacher(){
    19        return isUserRole("teacher");
    20      }
    21      
    22      function isStudent(){
    23        return isUserRole("student");
    24      }
    25
    26      function isHomeroomTeacher(){
    27        return request.auth.uid == resource.data.homeroomTeacher
    28      }
    29
    30      allow read: if isAdmin();
    31
    32      allow read: if isTeacher()
    33                      && isHomeroomTeacher();
    34
    35      allow read: if isStudent();
    36    }
    37  }
    38}

    ここで、teacher ユーザに対する許可条件に、更新ルールの実装時に定義した isHomeroomTeacher() 関数を追加して、担任チェックを行っています。

    テスト結果

    1 FAIL  tests/data.read.test.ts
    2  成績データの取得テスト
    3adminユーザはデータの取得可 (2124ms)
    4ログインしていないユーザは取得不可 (125ms)
    5    teacherユーザの取得要件
    6要件にあったデータの取得に成功 (74ms)
    7担任でないデータは取得不可 (95ms)
    8    studentユーザの取得要件
    9要件にあったデータの取得に成功 (94ms)
    10      × 自分以外のデータは取得不可 (102ms)
    11      × 対象シーズンが成績評価期間の場合は取得不可 (75ms)

    teacherユーザの取得要件 テストが全て通りました。

    student ユーザの取得要件をルールに設定

    続いて、studentユーザの取得要件 テストを通していきます。

    以下のように、ルールを調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      function isUserRole(role){
    10        return isLoggedIn()
    11                && role in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
    12      }
    13
    14      function isAdmin(){
    15        return isUserRole("admin");
    16      }
    17
    18      function isTeacher(){
    19        return isUserRole("teacher");
    20      }
    21      
    22      function isStudent(){
    23        return isUserRole("student");
    24      }
    25
    26      function isHomeroomTeacher(){
    27        return request.auth.uid == resource.data.homeroomTeacher
    28      }
    29
    30      function isEvaluationPeriod(){
    31        return get(/databases/$(database)/documents/seasons/$(resource.data.season)).data.evaluationPeriod
    32      }
    33
    34      // 本人データチェック
    35      function isSelfData(){
    36        return request.auth.uid == studentId
    37      }
    38      
    39      allow read: if isAdmin();
    40
    41      allow read: if isTeacher()
    42                      && isHomeroomTeacher();
    43
    44      allow read: if isStudent()
    45                      && isSelfData()
    46                      && !isEvaluationPeriod();
    47    }
    48  }
    49}

    ここで、student ユーザの許可条件に、本人データチェック関数 isSelfData() と、データ更新ルールの実装時に定義した成績評価期間チェック関数 isEvaluationPeriod() を追加しています。

    isSelfData() 関数では、リクエストユーザと「match ステートメント」中の studentId 変数が一致するかをチェックし、リクエストユーザ本人のデータかどうかを確認しています。

    成績評価期間のチェックでは、isEvaluationPeriod() に論理否定演算子「!」を添えて、評価期間外の取得を許可しています。

    テスト結果

    1 PASS  tests/data.read.test.ts
    2  成績データの取得テスト
    3adminユーザはデータの取得可 (2134ms)
    4ログインしていないユーザは取得不可 (117ms)
    5    teacherユーザの取得要件
    6要件にあったデータの取得に成功 (95ms)
    7担任でないデータは取得不可 (99ms)
    8    studentユーザの取得要件
    9要件にあったデータの取得に成功 (97ms)
    10自分以外のデータは取得不可 (95ms)
    11対象シーズンが成績評価期間の場合は取得不可 (63ms)

    全てのテストが通りました!

    以上で、データ取得用のルールは実装完了です。

    次のテストとの競合を避けるため、テストを終了してください。

    Firestore 上のデータ削除ルールの実装について

    テストの作成

    次に、データ削除ルールを実装します。

    ここまでのルール実装と同様、テストを作成する前に、まずはデータ削除の要件を再確認します。

    1. admin ユーザ以外は削除不可

    上記の要件に沿って、テストを作成します。

    tests/data.delete.test.ts ファイルを追加して、以下のようにコードを記述してください。

    テストのコード

    1process.env.FIRESTORE_EMULATOR_HOST = "localhost:58080";
    2
    3import { FirestoreTestSupporter } from "firestore-test-supporter";
    4import * as path from "path";
    5import * as firebase from "@firebase/testing";
    6import { users } from "./data/collections/users";
    7import { records } from "./data/collections/records";
    8import InitialData from "./data/InitialData";
    9
    10describe("成績データの削除テスト", () => {
    11    const rulesFilePath = path.join(__dirname, "firestore.rules");
    12    const supporter = new FirestoreTestSupporter("my-test-project", rulesFilePath);
    13
    14    beforeEach(async () => {
    15        await supporter.loadRules();
    16
    17        // 各コレクションに初期データを追加
    18        const initialData = new InitialData(rulesFilePath);
    19        await initialData.setupUsers();
    20        await initialData.setupRecords();
    21        await initialData.setupSeasons();
    22    });
    23
    24    afterEach(async () => {
    25        await supporter.cleanup()
    26    });
    27
    28    describe("adminユーザ以外は削除不可", () => {
    29        test("ログインしていないユーザはデータの削除不可", async () => {
    30            // 認証されていないクライアントを取得
    31            const db = supporter.getFirestore();
    32
    33            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    34            await firebase.assertFails(doc.delete());
    35        });
    36
    37        test("adminユーザはデータの削除に成功する", async () => {
    38            // adminユーザで認証されたクライアントを取得
    39            const db = supporter.getFirestoreWithAuth(users.sample.admin);
    40
    41            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    42            await firebase.assertSucceeds(doc.delete());
    43        });
    44
    45        test("teacherユーザはデータの削除不可", async () => {
    46            // teacherユーザで認証されたクライアントを取得
    47            const db = supporter.getFirestoreWithAuth(users.sample.teacher);
    48
    49            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    50            await firebase.assertFails(doc.delete());
    51        });
    52
    53        test("studentユーザはデータの削除不可", async () => {
    54            // studentユーザで認証されたクライアントを取得
    55            const db = supporter.getFirestoreWithAuth(users.sample.student);
    56
    57            const doc = db.collection(records.collectionPath).doc(records.initialData.id);
    58            await firebase.assertFails(doc.delete());
    59        })
    60    })
    61});

    テストの概要

    認証されていないユーザ、teacher ユーザ、student ユーザのそれぞれについて、データの削除不可テストを行います。

    admin ユーザについては、要件に沿って削除が成功するかどうかをチェックします。

    Firestore 上のデータ削除ルールを実装してみる

    続いて、作成したテストに沿って、セキュリティルールを実装していきます。

    ここまでのルール実装と同様、データ削除に関する部分だけを実装し、その後、これまでに作成したルールと統合します。

    全ての delete アクセスを許可するルールを設定

    削除ルールについては、簡単にルールを調整する程度なので、最初にルールを作成し、続いて、テストをパスするかを簡単に確認します。

    以下のように、セキュリティルールを調整してください。

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      function isUserRole(role){
    10        return isLoggedIn()
    11                && role in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
    12      }
    13
    14      function isAdmin(){
    15        return isUserRole("admin");
    16      }
    17
    18      allow delete: if isAdmin();
    19    }
    20  }
    21}

    データ追加ルールの実装で定義した isAdmin() 関数を使って、admin ユーザのみデータの削除を許可しています。

    テストをスタート

    以下のコマンドを実行して、データ取得ルール用のテストをスタートしてください。

    1npm run test tests/data.delete.test.ts

    テスト結果

    1 PASS  tests/data.delete.test.ts
    2  成績データの削除テスト
    3    adminユーザ以外は削除不可
    4ログインしていないユーザはデータの削除不可 (2210ms)
    5adminユーザはデータの削除に成功する (124ms)
    6teacherユーザはデータの削除不可 (112ms)
    7studentユーザはデータの削除不可 (113ms)

    全てのテストが通りましたので、以上でデータ削除用のルールは実装完了です。

    リグレッションテスト

    最後に、これまでの記事で実装したルールを統合し、全てのテストを再度実行します。

    テストにより、ルールの調整を進める中で「ルールが要件から外れていないか?」確認します。

    前回までに実装したルールへ、今回作成したデータの取得・削除ルールを統合すると以下のようになります。

    全てのルールを統合してテストを再実行

    1rules_version = '2';
    2service cloud.firestore {
    3  match /databases/{database}/documents {
    4    match /students/{studentId}/records/{recordId}{
    5      function isLoggedIn(){
    6        return request.auth.uid != null;
    7      }
    8
    9      function isUserRole(role){
    10        return isLoggedIn()
    11                && role in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
    12      }
    13
    14      function isAdmin(){
    15        return isUserRole("admin");
    16      }
    17
    18      function isTeacher(){
    19        return isUserRole("teacher");
    20      }
    21      
    22      function isStudent(){
    23        return isUserRole("student");
    24      }
    25
    26      function isRecordEmpty(_resource){
    27        return _resource.data.record == {}
    28      }
    29
    30      function isValidFormat(){
    31        return request.resource.data.id is string
    32                && request.resource.data.studentId is string
    33                && request.resource.data.name is string
    34                && request.resource.data.season is string
    35                && request.resource.data.homeroomTeacher is string
    36                && request.resource.data.record is map
    37      }
    38
    39      function isNotUpdate(key){
    40        return request.resource.data[key] == resource.data[key]
    41      }
    42
    43      function isNotUpdateImmutables(){
    44        return isNotUpdate("id")
    45                && isNotUpdate("studentId")
    46                && isNotUpdate("season")
    47      }
    48
    49      function isHomeroomTeacher(){
    50        return request.auth.uid == resource.data.homeroomTeacher
    51      }
    52
    53      function isEvaluationPeriod(){
    54        return get(/databases/$(database)/documents/seasons/$(resource.data.season)).data.evaluationPeriod
    55      }
    56      
    57      function isSelfData(){
    58        return request.auth.uid == studentId
    59      }
    60
    61      allow create: if isAdmin()
    62                        && request.resource.data.record == {}
    63                        && isValidFormat();
    64      
    65      allow update: if isAdmin()
    66                        && isNotUpdateImmutables()
    67                        && isNotUpdate("record")
    68                        && isValidFormat();
    69
    70      allow update: if isTeacher()
    71                        && isHomeroomTeacher()
    72                        && isNotUpdateImmutables()
    73                        && isNotUpdate("name")
    74                        && isNotUpdate("homeroomTeacher")
    75                        && isEvaluationPeriod()
    76                        && isValidFormat();
    77
    78      allow read: if isAdmin();
    79
    80      allow read: if isTeacher()
    81                      && isHomeroomTeacher();
    82
    83      allow read: if isStudent()
    84                      && isSelfData()
    85                      && !isEvaluationPeriod();
    86
    87      allow delete: if isAdmin();
    88    }
    89  }
    90}

    全てのテストを実行

    以下のコマンドを実行して、ここまでに追加した全てのテストを実行してください。

    1npm run test

    テスト結果

    1 PASS  tests/data.update.test.ts (5.817s)
    2 PASS  tests/data.read.test.ts
    3 PASS  tests/data.delete.test.ts
    4 PASS  tests/data.create.test.ts
    5
    6Test Suites: 4 passed, 4 total
    7Tests:       32 passed, 32 total
    8Snapshots:   0 total
    9Time:        10.156s, estimated 12s
    10Ran all test suites.

    全てのテストに成功しました。以上で、最初に設定した要件に沿ったルールを実装することができました。

    まとめ

    以上、成績評価システムを例に、成績コレクションのルールを実装すると共に、セキュリティルールでのカスタム関数の利用について解説してみました。

    最後に、今回の内容を簡単にまとめてみたいと思います。

    1. カスタム関数で条件に名前付け!
    2. カスタム関数で重複回避!
    3. カスタム関数で条件が再利用できる!
    4. カスタム関数でセキュリティルールがスッキリ!
    5. 締めのリグレッションテスト!

    これにて、『カスタム関数編』は終了となります!

    4回にわたり、ご愛読いただきありがとうございます!

    次回の「テスト駆動で学ぶ Firestore セキュリティルール」の連載にもご期待ください!

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    featureImg2020.08.04エンジニアの働き方 特集社員としての働き方社員としてのエンジニアの働き方とは?ライトコードのエンジニアはどんな働き方をしてるのか、まとめたいと...

    featureImg2020.07.27IT・コンピューターの歴史特集IT・コンピューターの歴史をまとめていきたいと思います!弊社ブログにある記事のみで構成しているため、まだ「未完成状態」...

    データ検証編はこちら

    「データ検証編」では、リクエストデータ、データベース上のデータの参照などについて解説

    featureImg2020.04.22テスト駆動で学ぶ Firestore セキュリティルール 【データ検証編 / 前編】テスト駆動で商品データのルールを実装するこの記事では、セキュリティルールによるデータ検証について、商品データのルールを...

    データ比較編はこちら

    「データ比較編」では、データの比較や型チェックなどについて解説

    featureImg2020.05.07テスト駆動で学ぶ Firestore セキュリティルール 【データ比較編 / 前編】テスト駆動で書籍コレクションのルールを実装するこの記事では、前編・後編の二回に分けて、Firestore セキュリティ...

    カスタム関数編はこちら

    「カスタム関数編」では、「比較編」で作成したルールをもとに、カスタム関数の使い方を解説

    featureImg2020.06.04テスト駆動で学ぶ Firestore セキュリティルール【カスタム関数編:第1回】テスト駆動で成績データコレクションのルールを実装する【第1回】この記事では、全4回に分けて、Firestore セキュ...

    広告メディア事業部

    広告メディア事業部

    おすすめ記事