56
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

権限管理のgemをbankenに乗り換えてみた

Posted at

この記事をみて、さっそくbankenの乗り換えてみた。

準備

bankenのREADME.mdにある通りGemfileに追記して、

Gemfile
gem 'banken'

gemとApplicationLoyaltyをインストールをする。

$ bundle install
$ bundle exec rails g banken:install

乗り換えるにあたっての手抜き

乗り換えるにあたり、なるべくコード量を減らしたかったので、手抜きのためのコードを書いた。

ApplicationLoyalty#allow_all?

「この権限ならこのコントローラー全部アクセスできるでしょ!」っていうのがあると思うので、ApplicationLoyaltyallow_all?というメソッドを追加して、各メソッドはその値を参照するようにした。

app/loyalties/application_loyalty.rb
class ApplicationLoyalty
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    allow_all?
  end

  def show?
    allow_all?
  end

  def create?
    allow_all?
  end

  def new?
    create?
  end

  def update?
    allow_all?
  end

  def edit?
    update?
  end

  def destroy?
    allow_all?
  end

  private

  def allow_all?
    false
  end
end

使うときはこんな感じ。「この権限なら」の権限毎にallow_all?を定義したモジュールを用意してインクルードしてる。

# app/loyalties/concerns/allow_no_signed_in_loyal.rb
module AllowNotSignedInLoyal
  def allow_all?
    true
  end
end

# app/loyalties/concerns/allow_singed_in_loyal.rb
module AllowSignedInLoyal
  def allow_all?
    user.present?
  end
end

# app/loyalties/welcome_loyalty.rb
class WelcomeLoyalty < ApplicationLoyalty
  include AllowNotSignedInLoyal
end

# app/loyalties/posts_loyalty.rb
class PostsLoyalty < ApplicationLoyalty
  include AllowSignedInLoyal

  # 条件個別に定義することもできる。
  def index?
    true
  end
end

「ログインしていればOK」「管理者権限があればOK」とかそういう大味な権限管理の場合に使う。

アクションのメソッドを定義するのをやめる

  • いちいちアクションを定義するのがだるい1
  • 水際で食い止めるのはallow_all?でいいじゃん

という気持ちがあったので、method_missingBanken#banken_query_name(実際は?で終わるメソッド)に自動で応答するようにした。

app/loyalties/concerns/dynamic_loyal.rb
module DynamicLoyal
  def method_missing(method_name, *argments, &block)
    if method_name.to_s.end_with?('?')
      allow_all?
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    method_name.to_s.end_with?('?') || super
  end
end

やんちゃなのでApplicationLoyaltyでincludeしちゃう。

app/loyalties/application_loyalty.rb
class ApplicationLoyalty
  include DynamicLoyal

  # snip ...
end

権限は専用のオブジェクトにまとめて移譲する。

bankenは「権限とコントローラー、ビューの紐付けをするgem」で「権限自体を定義するgemではない」って認識なので、HogeLoyaltyに具体的な権限の定義を書くのはやめてAbilityのようなクラスにまとめて定義した。2

app/models/ability.rb
class Ability
  attr_reader :user

  def initialize(user)
    @user = user
  end

  def not_singed_in?
    true
  end

  def signed_in?
    user.present?
  end

  def admin?
    user.try(:admin?)
  end

  def update_post?(post)
    admin? || post.unpublished?
  end
end

確認はAbilityに委譲する。

class ApplicationLoyalty
  def ability
    @ability ||= Ability(user)
  end
end

module AllowSignedInLoyal
  def allow_all?
    ability.signed_in?
  end
end

class PostsLoyalty < ApplicationLoyalty
  include AllowSignedInLoyal

  def index?
    ability.not_singed_in?
  end

  def update?
    ability.update_post?(record)
  end
end

テストをかく

愚直に書くとcontroller_nameaction_nameが分離してしまってわかりにくいので

describe 'loyalty' do
  context 'user' do
    let(:user) { create(:user) }
    it { expect(Banken.loyalty!(:posts, user, create(:post))).to be_update }
  end
end

ヘルパークラス書いた。

# spec/support/banken_permission.rb
class BankenPermission
  attr_reader :user

  def initialize(user)
    @user = user
  end

  def allow?(controller_name, action_name, record = nil)
    Banken.loyalty!(controller_name, user, record).public_send("#{action_name}?")
  end
end

describe 'loyalty' do
  context 'user' do
    subject { BankenPermission.new(create(:user)) }
    it { should be_allow(:posts, :update, create(:post)) }
  end
end

乗り換えてみて

乗り換え前は#386 Authorization from Scratch Part 2コードを使っていた。これと比べると、

Pros.

  • 権限周りの修正の影響範囲が狭くなった。
    • コントローラーごとに別クラス、別ファイルなので影響しにくい
    • コンフリクトに強そう。diffが見やすそう。
  • 定義順に依存とかそういうのを気にする必要が無くなった。

Cons.

  • コントローラーの数だけLoyaltyを用意するし記述が冗長。
    • 冗長だけど、3行で済むLoyaltyも多いので何とかなりそう。
  1. アクセス制御にあるまじき発想だけど、、、許して、、

  2. Abilityの実装にはcancancansix(gitlabの権限管理gem)のような権限を定義するgemが使える。

56
52
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
56
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?