Rails管理画面生成gem administrate を使ってみる

この手のものはrails_adminActive Adminなどが有名ですが、 DSLを覚える必要があったり色々癖があって選択が難しいです。
 
どれを選ぼうか迷っていたら、thoughtbot/administrate というものを見つけました。
 
paperclipfactory_girlなどで有名な thoughtbot社製で、DSL不要とのこと。
ちょっと使ってみたところ、なかなか使い勝手が良く気に入りました。
 

How to use it

コマンド一発で各種ファイルをadmin以下に生成してくれます。これでもう使えてしまいます。

$ rails generate administrate:install

新しいモデルを作成して途中で追加する場合は、以下を実行すれば良いです。

$ rails generate administrate:dashboard Foo

あとは config.routes にresourcesを追加するだけ。

Rails.application.routes.draw do
  namespace :admin do
    resources :players
    resources :foos
  end
end

こんな感じです。
関連テーブルもリンクが生成されますし、selectで表示したり、truncateしたりというのも可能です。
f:id:rochefort:20170508150929p:plain

Costomize

この辺りにまとまっています。 https://administrate-prototype.herokuapp.com

dashboard

dashboard というファイルに 項目の表示する型を定義し、画面ごとにどの項目を表示するかという設定がhash/arrayでできます。
ここで表示させたいものを表示させたい順に定義するだけで、画面の表示内容が変わります。非常に簡単で良いです。
デフォルトは項目が絞られているので、ここの修正は必須です。
 

custom field type

Gravatorのアイコンを表示させるみたいなのは、custom field type として作成しておけば、dashboardで指定するだけで使えます。
https://administrate-prototype.herokuapp.com/adding_custom_field_types

css/js

issueに記載がありました。
How to add custom CSS? · Issue #748 · thoughtbot/administrate

# config/initializers/administrate.rb
Administrate::Engine.add_stylesheet('your_stylesheet_name')

asset pipelineに乗せるなら、以下。

# config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(your_stylesheet_name.css)

ちなみに

各種Gemのdownload数は、

$ gem-search -e rails_admin
Searching .
NAME                                                DL(ver)   DL(all) HOMEPAGE
-------------------------------------------------- -------- --------- ------------------------------------------------------------
rails_admin (1.1.1)                                   81127   1354125 https://github.com/sferik/rails_admin

$ gem-search -e activeadmin
Searching .
NAME                                                DL(ver)   DL(all) HOMEPAGE
-------------------------------------------------- -------- --------- ------------------------------------------------------------
activeadmin (1.0.0)                                    6073   2105976 http://activeadmin.info

$ gem-search -e typus
Searching .
NAME                                                DL(ver)   DL(all) HOMEPAGE
-------------------------------------------------- -------- --------- ------------------------------------------------------------
typus (3.1.10)                                        11001    152308 http://www.typuscmf.com/

$ gem-search -e administrate
Searching .
NAME                                                DL(ver)   DL(all) HOMEPAGE
-------------------------------------------------- -------- --------- ------------------------------------------------------------
administrate (0.6.0)                                    642    139972 https://administrate-prototype.herokuapp.com/

activeadmin 圧勝。

See Also

Rails管理画面生成gem administrate でソート順を指定する - rochefort’s blog

Railsで配列の値を like or 検索する

like検索時に配列の値をorで簡単に複数検索できないものかと調べてみたら ReneB/activerecord-like というgemで実現できました。

How to use it

単一カラムでのlike

words = %w(フロンターレ 中村憲剛)
like_words = words.map{ |word| "%#{word}%" }
puts Product.where.like(title:  like_words).to_sql
SELECT "products".* FROM "products"
WHERE ("products"."title" LIKE '%フロンターレ%' OR "products"."title" LIKE '%中村憲剛%')

複数カラムでのlike検索

or 検索で繋げていく方法。

words = %w(フロンターレ 中村憲剛)
like_words = words.map{ |word| "%#{word}%" }
puts Product.where.like(title:  like_words).or(Product.where.like(authors: like_words)).to_sql
SELECT "products".* FROM "products"
WHERE
(
  ("products"."title" LIKE '%フロンターレ%' OR "products"."title" LIKE '%中村憲剛%')
  OR
  ("products"."authors" LIKE '%フロンターレ%' OR "products"."authors" LIKE '%中村憲剛%')
)

他にもnot_likeが使えるようになります。
全文検索を用意するほどでもないぐらいの規模ならありかもしれません。
ソース読んでみましたが、DSL全開でぱっと見理解できませんでした。activerecordの勉強が必要そうです。

RailsでDBの値を暗号化する

DB保存時に個人情報的なやつは暗号化しておきたいという要望はよくあるかと思います。
今回はattr_encrypted というgemを利用してみました。
自前で実装するなら ActiveSupport::MessageEncryptor が利用できるようですのでこちらも少し試してみました。

まずはActiveSupport::MessageEncryptor

以下のような module を作成し、(cipherについては後述)
encrypt_secure_key は secrets.yml に定義。

module Encryptor
  CIPHER = "aes-256-cbc"
  def encrypt(password)
    secure = Rails.application.secrets.encrypt_secure_key
    crypt = ActiveSupport::MessageEncryptor.new(secure, CIPHER)
    crypt.encrypt_and_sign(password)
  end

  def decrypt(password)
    secure = Rails.application.secrets.encrypt_secure_key
    crypt = ActiveSupport::MessageEncryptor.new(secure, CIPHER)
    crypt.decrypt_and_verify(password)
  end
end

以下のようにmodel側でincludeすれば暗号化できます。復号化もdecrypt呼ぶだけ。

class User < ApplicationRecord
  include Encryptor
  before_save :encrypt_collumns

  def encrypt_collumns
    self.address = encrypt(self.address)
  end
end

簡単に実装はできましたが、decryptするのが少し面倒。self.address を decryptした項目を返すようにすれば良さそうですが
項目増えるたびにメソッド書くのもなぁという気がします。
 

次は attr_encrypted

使い方

例えば ssn という項目を暗号化させたい場合、encrypted_ssnencrypted_ssn_iv を用意します。

  create_table :users do |t|
    t.string :name
    t.string :encrypted_ssn
    t.string :encrypted_ssn_iv
    t.timestamps
  end

モデル側で以下のようにすればok。簡単に利用できて良いです。

class User
  attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
end

補足

  • 項目のPrefixencrypted_は変更可能
  • iv について
    そもそもivは、暗号化の初期ベクトルのことです。暗号化時には指定されたブロック単位で暗号化して行きますが、前のブロックの暗号化データを排他的論理和で論理演算するという仕組みですが、この初期の暗号化時には前のブロックデータがないので、代わりに与えるのがivとのこと。
  • modeについて
    mode というオプションがあり、現在デフォルトはper_attribute_iv というivが必要なものが設定されています。
    per_attribute_iv_and_saltsingle_iv_and_salt というものも現在は使用できますが、次回メジャーバージョンアップ時にはなくなる予定とのこと。

 
以下余談です。

Cipher Suites について

ActiveSupport::MessageEncryptor のデフォルトは aes-256-cbc でした。
attr_encrypted は aes-256-gcm です。
ほとんどの人はデフォルトのものを利用するでしょうし、このような用途では通常ivなどの値がhttpのデータとして通信されることはないので、これで良い気がしますが、少し気になったので調べてみます。
 
使用可能なcipherは以下で確認できます。

require 'openssl'
puts OpenSSL::Cipher.ciphers

aes-256だけでも沢山あり、どれ選んでいいかさっぱりわかりません。

aes-256-cbc
aes-256-cbc-hmac-sha1
aes-256-cbc-hmac-sha256
aes-256-ccm
aes-256-cfb
aes-256-cfb1
aes-256-cfb8
aes-256-ctr
aes-256-ecb
aes-256-gcm
aes-256-ofb
aes-256-xts

Stack Overflow

Stack Overflow に一部解説がありました。
How to choose an AES encryption mode (CBC ECB CTR OCB CFB)? - Stack Overflow

  • ECB should not be used if encrypting more than one block of data with the same key.
    同一キーで複数ブロック暗号化するなら使用するな
  • CBC, OFB and CFB are similar, however OFB/CFB is better because you only need encryption and not decryption, which can save code space.
    OFB/CFB は復号化を必要としないなら省スペース。
  • CTR is used if you want good parallelization (ie. speed), instead of CBC/OFB/CFB.
    CTRは並列化が必要なら利用される。
  • XTS mode is the most common if you are encoding a random accessible data (like a hard disk or RAM).
    XTSはランダムアクセス時に最も利用される。
  • OCB is by far the best mode, as it allows encryption and authentication in a single pass. However there are patents on it in USA.
    OCBは効率よく一番いいけど、特許の問題がある。

他にも、uniqueなivが使えないならOCBを使えとあります。
コメントには、GCMはOCBにとても似ており、特許の問題がないので一番良い選択肢だとあります。(ただし実装が複雑)
 
GCMで良い気がしてきました。

一応ivの生成を確認してみます。

# attr_encrypted-3.0.3/lib/attr_encrypted.rb
      def generate_iv(algorithm)
        algo = OpenSSL::Cipher.new(algorithm)
        algo.encrypt
        algo.random_iv
      end

random_iv は以下。

# ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
def random_iv
  str = OpenSSL::Random.random_bytes(self.iv_len)
  self.iv = str
end

強度は iv_len で決まりますが、値は12でした。
これが強いのか弱いのか、、、よくわかりません。

("\x00".."\xff").to_a.size
=> 58

なので、58の12乗ということなのかしら。まぁまぁ大丈夫そう。