设计可锁定和自定义策略

时间:2016-01-12 19:22:31

标签: ruby-on-rails-4 authentication devise

我正在为LDAP身份验证开发自定义策略,并且我已根据http://kyan.com/blog/2013/10/11/devise-authentication-strategies和此设计wiki实施了valid?authenticate!方法。我的自定义策略独立运作。

我也想使用Lockable模块(和其他Devise模块),但它们似乎只在Database_Authenticatable也存在的情况下才有用。我一直在阅读Devise源代码,但我不明白Lockable如何能够找到用户(资源)以增加:failed_attempts计数。如何使用自定义策略将用户记录或资源传递给Lockable?

我的自定义策略如下。我正在使用Devise 3.5和Rails 4.2

require 'net/ldap'
require 'devise/strategies/authenticatable'

module Devise
  module Strategies
   class LdapAuthenticatable < Authenticatable


  def valid?
    params[:user] && login.present? && password.present?
  end

  def authenticate!

    resource = mapping.to.find_for_authentication(email: params[:user][:email])

    if params[:user]
      begin
        ldap = Net::LDAP.new
        ldap.auth(my_ldap_service, my_ldap_service_password)
        ldap.encryption(:simple_tls)
        ldap.base = "base_here"
        ldap.host = my_ldap_host
        ldap.port = "636"
        result_attrs = ["employeenumber", "givenname", "sn", "mail", "department"] 

        result = ldap.bind_as(base: "base_here", filter: "(mail=#{login})", attributes: result_attrs, password: password, time: 3) 


        if result
          #valid ldap credentials
          user = get_user_from_database(result)
          if user
            success!(user)
          else
            fail!(:invalid_login)
          end

        else 
          fail!(:invalid_login)
        end

      rescue => e
        Rails.logger.error e.message
        fail!(:invalid_login)
      end

    end
  end

  def login
    params[:user][:email]
  end

  def password
    params[:user][:password]
  end

end
end
end

3 个答案:

答案 0 :(得分:0)

Active Directory应该处理可锁定的功能。通过在自定义策略中使用LDAP,可以将功能的责任从应用程序转移到LDAP / Active Directory。可锁定应防止恶棍强行访问您的应用程序。如果Active Directory对此没有限制,他可以直接针对它进行攻击,如果成功,则使用应用程序中的凭据。

但是,您应该启用应用程序以显示远程服务器返回的错误消息,例如“重试次数过多,请在5分钟后重试”。

答案 1 :(得分:0)

我最终结合使用devise_custom_authenticatable和原始问题上发布的资源。为使锁定功能正常工作,必须存在database_authenticable。如果您拥有对LDAP的控制权,那么Toni的answer应该可以使用,但是当我提出问题时却没有。

答案 2 :(得分:0)

您缺少调用 validate 方法,该方法是您继承的 Authenticatable 类的一部分。 validate 方法从可锁定模块调用 valid_for_authentication? 方法,然后该方法负责增加尝试次数并锁定帐户。

在 database_authenticable 中,这通过使用返回布尔值的 valid_password? 解决了 here。在您的情况下,您需要在 validate(resource) { false } 不返回结果的情况下添加 bind_as

固定的完整 authenticate! 方法如下所示

def authenticate!

    resource = mapping.to.find_for_authentication(email: params[:user][:email])

    if params[:user]
      begin
        ldap = Net::LDAP.new
        ldap.auth(my_ldap_service, my_ldap_service_password)
        ldap.encryption(:simple_tls)
        ldap.base = "base_here"
        ldap.host = my_ldap_host
        ldap.port = "636"
        result_attrs = ["employeenumber", "givenname", "sn", "mail", "department"] 

        result = ldap.bind_as(base: "base_here", filter: "(mail=#{login})", attributes: result_attrs, password: password, time: 3) 


        if result
          #valid ldap credentials
          user = get_user_from_database(result)
          if user
            success!(user)
          else
            fail!(:invalid_login)
          end

        else
          # no result -> wrong password
          validate(resource) { false }
          fail!(:invalid_login)
        end

      rescue => e
        Rails.logger.error e.message
        fail!(:invalid_login)
      end

    end
  end