deviseの認証機能にIPアドレス制限をかける

認証機能にIPアドレス制限かけたいだけなので、いかようにもできそうですが、 オシャレに実装する方法はないかと調べてみると、以下の実装がありました。

routesのconstraints

ruby on rails 3 - How can I restrict access ActiveAdmin login page by ip? - Stack Overflow

class WhitelistConstraint
  def initialize
    @ips = Whitelist.retrieve_ips
  end
 
  def matches?(request)
    @ips.include?(request.remote_ip)
  end
end
 
# config/routes.rb
TwitterClone::Application.routes.draw do
  match 'admin/' => 'admin#login',
    :constraints => WhitelistConstraint.new
end

実装は簡単で、matches?メソッドが実装されたclassを用意し、routesのconstraintsオプションに渡すだけです。

deviseでやるには

単純にこの方法でやってみましたが、devise_for 自体がこの方法に対応してなさそうでした。

blockを使う

constraintsはblockで使うことができます。
Rails のルーティング - Railsガイド

namespace :admin do
  constraints subdomain: 'admin' do
    resources :photos
  end
end

最終的には

こうなりました。
constraintsにはクラス自体も渡せます。この場合は、クラスメソッドとしてmatches? を実装すれば良いです。

# lib/constraints/whitelist_constraint.rb
module Constraints
  class WhitelistConstraint
    WHITELIST_IPS = [
      "1.2.3.4",
      "5.6.7.8"
    ].freeze

    def self.matches?(request)
      Rails.env.development? || WHITELIST_IPS.include?(request.remote_ip)
    end
  end
end

# config/routes.rb
  constraints Constraints::WhitelistConstraint do
    devise_for :user, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout" }
  end

感想

constraints使ったことあんまりなかったのですが、なかなか便利でした。