Step8: セキュリティを高めよう!(Meteor methods)

このステップではMeteorのmethodsについて学びます。

insecure パッケージ

Meteorのアプリを新規作成すると、標準でinsecureというMeteorパッケージが含まれています。

これは、Webブラウザ側でMeteorコレクションのCRUD(Create, Read, Update, Delete)操作をできるようにするもので、だれでもコレクションを書き換えることができてしまいます。

クライアント側で勝手にデータを書き換えられては困るので、本番感環境ではinsecureパッケージを外し、Meteorのmethodsという機能を使用します。

それではMemosコレクション操作をmethodsを使って書き換えてみましょう。

まず、insecureパッケージを取り除きます。

$ meteor remove insecure

またmethodsの引数バリデーションに使用するaldeed:simple-schemaパッケージを追加します。

$ meteor add aldeed:simple-schema

続いて、Memosコレクション用のmethodsファイルを作成します。

$ touch imports/api/memos/methods.js

imports/api/memos/methods.js

import { Meteor } from 'meteor/meteor';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { Memos } from './memos';

Meteor.methods({
  'Memos.insert'(content) {
    new SimpleSchema({
      content: { type: String }
    }).validate({ content });

    this.unblock();

    Memos.insert({content});
  },

  'Memos.update'(memoId, content) {
    new SimpleSchema({
      memoId: { type: String },
      content: { type: String }
    }).validate({ memoId, content });

    this.unblock();

    Memos.update({_id: memoId}, {$set: {content}});
  },

  'Memos.remove'(memoId) {
    new SimpleSchema({
      memoId: { type: String }
    }).validate({ memoId });

    this.unblock();

    Memos.remove(memoId);
  }
});

サーバーでmethodsが実行できるようにmethodsファイルを読み込みます。

server/main.js

import { Meteor } from 'meteor/meteor';
import { Memos } from '../imports/api/memos/memos';
import '../imports/api/memos/methods';

Meteor.startup(() => {
  if (Memos.find().count() === 0) {
    const data = [
      {content: 'Memo 1'},
      {content: 'Memo 2'},
      {content: 'Memo 3'},
      {content: 'Memo 4'},
      {content: 'Memo 5'},
    ];
    data.forEach(memo => Memos.insert(memo));
  }
});

クライアント側からのコレクション操作を無効にするため、MemosコレクションにMeteor.deny()を設定します。

imports/api/memos/memos.js

import { Mongo } from 'meteor/mongo';

class MemosCollection extends Mongo.Collection {
  insert(doc, callback) {
    doc.createdAt = doc.createdAt || new Date();
    const result = super.insert(doc, callback);
    return result;
  }
}

export const Memos = new MemosCollection('Memos');

Memos.deny({
  insert() { return true; },
  update() { return true; },
  remove() { return true; },
});

// for debug
if (Meteor.isDevelopment) {
  global.collections = global.collections || {};
  global.collections.Memos = Memos;
}

最後にAppContainerのコレクション操作を行っている部分をmethodsMeteor.callで呼び出すように変更します。

imports/ui/containers/AppContianer.js

import AppLayout from '../layouts/AppLayout';
import { Memos } from '../../api/memos/memos';
import { createContainer } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';

const createMemo = content => {
  Meteor.call('Memos.insert', content);
};

const removeMemo = memoId => {
  Meteor.call('Memos.remove', memoId);
};

const updateMemoContent = (memoId, content) => {
  Meteor.call('Memos.update', memoId, content);
};

export default createContainer(() => {
  return {
    memos: Memos.find({}, {sort: {createdAt: -1}}).fetch(),
    createMemo,
    removeMemo,
    updateMemoContent,
  };
}, AppLayout);

コードを書き換えたらメモの追加、削除、更新ができることを確認しましょう。

なお、insecure パッケージを取り除いたことにより、クライアント側でコレクションに対してinsert, update, removeを実行するとエラーが発生するようになりました。

Webブラウザのインスペクタのコンソールで以下のコードを実行することで、挙動を確かめることができます。

collections.Memos.insert({content: 'New Memo'})
// => insert failed: Access denied

Meteor methods について詳しく知りたい方は、Meteor公式のReactチュートリアルおよびMeteor GuideのSecurityの章をご参照ください。

次のステップに進みましょう。

次へ