Solidusでロール別権限管理をする

Solidus（Spree）で役職別権限管理する方法についてです。

Solidusでは、初期の状態ではadmin(管理者)とdefault(エンドユーザー)の2つのRoleしか用意されていませんが、わずかなコードを書き足すことによってオリジナルの権限ロールを設定することが可能です。

例えば商品情報のみを編集する`editor`、注文情報のみを閲覧できる`staff`などです。この機能はSolidusに標準で組み込まれているgemのdeviseとcancanによって実現されており、コードに少し手を加えることで独自の権限ロールを作成することができるようになっています。deviseとcancanについての説明はここでは割愛しますが、実装前に基本的な部分はおさえておいたほうが良いと思うのでそれぞれのgemの公開されているドキュメント等を流し読みしておくとよいかもしれません。

なおSolidus（Spree）のRoleについての公式ドキュメントはここから閲覧できます（英語）

[SECURITY - DEVELOPER GUIDE | SPREE COMMERCE](https://guides.spreecommerce.com/developer/security.html)

なおこのドキュメントは以前私が翻訳した日本語版もあります。

[[日本語訳] SECURITY - DEVELOPER GUIDE | SPREE COMMERCE](http://qiita.com/yuskamiya/items/c831b621097e7a6d85c5)


## 新規Roleの作成

### DBへroleを保存

roleはSpree::Roleクラスのインスタンスで管理され、Spree::Userに対して一対多で紐づく形となります。consoleで`Spree::Role.all`とするとDBに保存されているRoleが全て閲覧できます。初期状態のSolidusでは`admin`のroleが1つあるだけのはずです。新しいRoleを作成するときは、まず`Spree::Role.create(name: "staff")`などとして新しいRoleをDBに保存する必要があります。この作業はSolidusの管理画面からはできないのでコンソールから行う必要があります。

### roleへの権限割当

roleへの権限割当の方法は、Solidusのソースコード内にコメントとして記載されていました。以下の書式で新しいroleに権限を付与することが可能です。

[solidus/core/lib/spree/core/role_configuration.rb](https://github.com/solidusio/solidus/blob/v1.2/core/lib/spree/core/role_configuration.rb)

```rb
# A class responsible for associating {Spree::Role} with a list of permission sets.
#
# @see Spree::PermissionSets
#
# @example Adding order, product, and user display to customer service users.
#   Spree::RoleConfiguration.configure do |config|
#     config.assign_permissions :customer_service, [
#       Spree::PermissionSets::OrderDisplay,
#       Spree::PermissionSets::UserDisplay,
#       Spree::PermissionSets::ProductDisplay
#     ]
#   end
```

この書式で定義したroleを`config/initializers`のディレクトリに配置することで新しい権限の登録が完了します。

実際には以下のような書式で設定を行いました。

```rb:config/initializers/permissons.rb
Spree::RoleConfiguration.configure do |config|
  config.assign_permissions :staff, [
    Spree::PermissionSets::DashboardDisplay,
    ....
  ]
end
```

なお、Solidusでは業務区分ごとにまとめられたパーミッションセットがあらかじめ用意されています。以下のディレクトリにまとめられたものがありますので、これらを`config.assign_permissions`にArray形式で与えるだけで簡単にroleの作成が可能となっています。便利ですね。

[solidus/core/lib/spree/permission_sets](https://github.com/solidusio/solidus/tree/60ee1dffc7c3932cd7652cc2b4566f08a12f74b8/core/lib/spree/permission_sets)

また、独自のモデルやアクションに対してのパーミッションを定義する場合には、この書式にならってパーミッションセットを作るのが便利です。実際には下記の書式で定義が可能でした。

```rb:lib/spree/permission_sets/staff_order_management.rb
module Spree
  module PermissionSets
    class StaffOrderManagement < PermissionSets::Base
      def activate!
        can :display, Spree::ReimbursementType
        can :manage, Spree::Order
        ....
      end
    end
  end
end
```

ここで定義した独自のパーミッションセットは、標準のそれと同様に`config.assign_permissions`で使用できます。

アクションごとの細かい設定についてはcancanの書式に準じるのでそちらのドキュメントをご覧いただくのがおすすめです。

## もう1つ設定が必要

上記の方法は正しいはずだったのですが、これだけでは正常に動作しませんでした。問題点を探していたところ、以下のようなドキュメントを発見しました。

> If you need to create custom controllers for your own models under the Admin namespace, you will need to manually specify the model your controller manipulates by defining a model_class method in that controller.

```rb
module Spree
  module Admin
    class WidgetsController < BaseController
      def index
        # Relevant code in here
      end

    private
      def model_class
        Widget
      end
    end
  end
end
```

admin namespace内に独自のコントローラを作っていた場合、上記のように`model_class`メソッドを定義してあげないと正しく動作しないようです。これを定義したところ正しく動作することが確認できました。

## ユーザーへの権限付与

新しいRoleの定義が完了したら、あとは管理画面から権限の付与が可能です。

`/admin/users/:id/edit`画面にroleのチェックボックスが表示されますので、ここから権限付与できます。

手軽に独自の権限ロールを設定できるのはありがたいですね。Eコマースは個人情報のような秘匿性の高い情報を扱う分、セキュリティの対策は十二分に施しておきたいところです。