• トップ
  • ブログ一覧
  • テスト駆動で学ぶ 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 セキュ...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background