LoginSignup
2
0

More than 1 year has passed since last update.

Web3.0検証(19)-MeteorでTODO管理アプリの開発(データアクセス制御)

Last updated at Posted at 2022-05-30
[前回] Web3.0検証(18)-MeteorでTODO管理アプリの開発(Githubアカウントでログイン)

はじめに

今回は、セキュリティ関連のデータ保護機能です。
Meteorメソッドを使用し、データへのアクセスを制御します。

データアクセス制御の必要性

  • アプリケーションで、データへのアクセスを制御しないと
  • クライアントからデータを直接操作できるなど、セキュリティ上問題となる

Meteorメソッドによるデータ操作

  • Meteorメソッドとは、Meteor.call関数を使用しサーバーと通信する方法
    • メソッドの名前と引数を指定する必要あり
  • Meteorでメソッドを宣言することで、安全にデータ操作できる
    • メソッドを使用し、ユーザーのデータ操作に対し認証/認可を行う
  • クライアントからの以下直接呼び出しをブロック
    • insert
    • update
    • remove

準備

  • 端末を二つ用意します。

    • 端末1: meteor起動/確認
    • 端末2: コード修正
  • 端末1から、Meteorアプリを実行

$ cd simple-todos-react
$ meteor run
  • ブラウザでアプリを確認
    • http://localhost:3000/にアクセス
    • デベロッパーツールを開く(F12)
    • デバイスのツールバーを切り替えアイコンをクリック
    • デバイスを選択
      • 今回は、iPhone SEを選択

安全でないパッケージを無効に

  • insecureパッケージ

    • クライアントからデータベースを編集可能にできる
    • 迅速なプロトタイピングに役立つ
  • 端末2から、安全でないパッケージを削除

$ cd simple-todos-react
$ meteor remove insecure
  • クライアント側のデータベース権限をすべて取り消したため
    • アプリの変更は機能しない
      • たとえば、新しいタスクを挿入すると失敗
        image.png

タスクメソッドを追加

  • 作業内容

    • メソッドを定義
      • クライアントで実行するデータベース操作ごとに1つのメソッドが必要
      • Optimistic UIをサポートするためには、クライアントとサーバー両方で実行されるコードで定義する必要あり
    • Optimistic UI
      • Meteor.callを使用し、クライアントでメソッドを呼び出すと、次の2つが並行して発生
        • クライアントはサーバーにリクエストを送信し、安全な環境でメソッドを実行
        • メソッドのシミュレーションがクライアントで直接実行され、呼び出しの結果を予測
        • サーバーから結果が返される前に、新しく作成されたタスクが実際に画面に表示される
        • 予測結果がサーバーの結果と一致する場合、すべてがそのまま残る
        • さもなければ、UIはサーバーの実際の状態を反映するため更新される
  • コード

    • imports/apiフォルダーにtasksMethodsファイルを追加
      • クライアントは、Mini Mongo操作を直接使用する代わりに、これらのメソッドを呼び出す
      • メソッド内に、オブジェクトで使用できる特別なプロパティが存在
        • たとえば、認証されたユーザーのuserId
    • checkパッケージを使用し、入力値が期待されるタイプか確認
      • データベースに対し、何を挿入/更新しているかを正確に把握するために重要
imports/api/tasksMethods.js
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { TasksCollection } from './TasksCollection';
 
Meteor.methods({
  'tasks.insert'(text) {
    check(text, String);

    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    TasksCollection.insert({
      text,
      createdAt: new Date,
      userId: this.userId,
    })
  },

  'tasks.remove'(taskId) {
    check(taskId, String);

    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    TasksCollection.remove(taskId);
  },

  'tasks.setIsChecked'(taskId, isChecked) {
    check(taskId, String);
    check(isChecked, Boolean);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    TasksCollection.update(taskId, {
      $set: {
        isChecked
      }
    });
  }
});
  • サーバーにこれらのメソッドが登録されているか確認
    • tasksMethodsファイルをインポート
    • インポートからシンボルを取得する必要なし
      • ファイルをインポートするようにサーバーに要求するだけで
        • Meteor.methodsが評価され、サーバー起動時にメソッドが登録される
server/main.js
import '/imports/api/tasksMethods';

メソッド呼び出しを実装

  • 作業内容
    • メソッドを使用するため、コレクションの操作箇所を更新
    • TaskFormファイルで
      • TasksCollection.insertの代わりに、Meteor.call('tasks.insert', text);を呼び出す
      • インポートも修正
      • サーバーでuserIdを取得する
        • TaskFormコンポーネントがユーザーを取得する必要はなくなる
  • コード
imports/ui/TaskForm.jsx
export const TaskForm = () => {
  const [text, setText] = useState('');

  const handleSubmit = e => {
    e.preventDefault();

    if (!text) return;

    Meteor.call('tasks.insert', text);

    setText('');
  };
  ... ...
};
  • App.jsxファイルで
    • TasksCollection.updateの代わりに、Meteor.call('tasks.setIsChecked', _id, !isChecked);をコール
    • TasksCollection.removeの代わりに、Meteor.call('tasks.remove', _id)`をコール
    • <TaskForm/>からユーザープロパティも削除
imports/ui/App.jsx
import { Meteor } from 'meteor/meteor';
import React, { useState, Fragment } from 'react';
import { useTracker } from 'meteor/react-meteor-data';
import { TasksCollection } from '/imports/db/TasksCollection';
import { Task } from './Task';
import { TaskForm } from './TaskForm';
import { LoginForm } from './LoginForm';

const toggleChecked = ({ _id, isChecked }) =>
  Meteor.call('tasks.setIsChecked', _id, !isChecked);

const deleteTask = ({ _id }) => Meteor.call('tasks.remove', _id);
... ...

            <TaskForm />
.....

アプリを確認

  • 今までの修正のおさらい

    • データベースにタスクを挿入すると
      • ユーザー認証によるチェックが実行される
    • クライアントコードをデータベースロジックから分離
      • データ変更は、イベントハンドラーの代わりに、メソッド呼び出しを使用
  • 画面から、新しいタスクを登録

image.png

タスクが正しく登録できました。やったー。

ファイル配置を変更

  • TasksCollection.jsファイルをapiフォルダからdbフォルダに移動

    • プロジェクトでAPIは、サーバーとクライアント間の通信レイヤーを意味
    • TasksCollectionコレクションは、上記修正により既にこの役割を果たしていない
    • ※ ファイルや変数の名前は現実と同じものが推奨される
  • インポートを修正

    • 以下四つのファイルにTasksCollectionへのインポートが存在
      • imports/api/tasksMethods.js
      • imports/ui/TaskForm.jsx
      • imports/ui/App.jsx
      • server/main.js
    • 変更前: import { TasksCollection } from '/imports/api/TasksCollection';
    • 変更後: import { TasksCollection } from '/imports/db/TasksCollection';

トラブルシューティング

  • Meteor DevToolsを使用

    • サーバーへ送信されるメッセージと返される結果を確認可能
    • ブラウザのデベロッパーツールで、[DDP]タブから確認可能
      • DDPは、Meteor通信レイヤーのバックグラウンドのプロトコル
        image.png
  • check呼び出しにエラーハンドリングを追加

    • 間違ったタイプに対し、発生したエラーを追跡できる

おわりに

アプリのセキュリティ対策として、データアクセス制御を追加しました。
次回も続きます。お楽しみに。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0