Devise ignoring custom strategy

2019-01-23 18:35发布

问题:

This is just plain weird.

I've got Rails 3 RC running with Devise installed. I've defined a custom strategy to try and use Kerberos for authentication.

module Devise
  module Strategies
    class Kerb < Devise::Strategies::Base
      def valid?
        params[:username] || params[:password]
      end

      def authenticate!
        # cheap debugging
        puts "PARAMS: #{params}"

        if check_kerb_auth(params[:username], params[:password])
          # create user account if none exists
          u = User.find(:first, :conditions => { :username => params[:username] }) || User.create({ :username => login })
          success!(u)
        else
          fail!("Could not log in")
        end
      end

      def check_kerb_auth(username, password)
        require 'krb5_auth'
        include Krb5Auth

        return false if username.blank? or password.blank?

        begin
            kerberos = Krb5.new
            return kerberos.get_init_creds_password(username, password)
        rescue Krb5Auth::Krb5::Exception
            return false
        end
      end
    end
  end
end

I have the Devise Warden configuration setup as follows:

config.warden do |manager|
  manager.strategies.add(:kerb, Devise::Strategies::Kerb)
  manager.default_strategies :kerb
end

I get no errors in my log. Everything seems to work ok. If I add "cheap debugging" aka a bunch of puts statements, it seems to reflect that the :kerb strategy is the default. Here is a sample set of logs from a login attempt:

=> Booting WEBrick
=> Rails 3.0.0.rc application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-08-17 10:50:35] INFO  WEBrick 1.3.1
[2010-08-17 10:50:35] INFO  ruby 1.8.7 (2010-01-10) [x86_64-linux]
[2010-08-17 10:50:40] INFO  WEBrick::HTTPServer#start: pid=12717 port=3000


Started POST "/users/login" for 127.0.0.1 at Tue Aug 17 10:50:43 -0400 2010
  Processing by Devise::SessionsController#create as HTML
  Parameters: {"commit"=>"Login", "authenticity_token"=>"afZF6ho96p47dc9LQFwwNN5PqnRpl7x+1J7V3MiKgTE=", "_snowman"=>"\342\230\203", "user"=>{"remember_me"=>"1", "username"=>"hernan43", "password"=>"[FILTERED]"}}
Completed   in 0ms
  Processing by Devise::SessionsController#new as HTML
  Parameters: {"commit"=>"Login", "authenticity_token"=>"afZF6ho96p47dc9LQFwwNN5PqnRpl7x+1J7V3MiKgTE=", "_snowman"=>"\342\230\203", "user"=>{"remember_me"=>"1", "username"=>"hernan43", "password"=>"[FILTERED]"}}
Rendered devise/shared/_links.erb (1.2ms)
Rendered devise/sessions/new.html.erb within layouts/application (8.2ms)
Completed 200 OK in 124ms (Views: 11.7ms | ActiveRecord: 1.3ms)

The kerberos code works in other things on the same machine. I was sort of expecting it to show a bunch of errors if there was a problem but I am getting nothing. Is there a good way to debug Devise/Warden?

回答1:

In case someone else comes across this, here's what I believe the problem is:

According to Warden Strategies:

valid?

The valid? method acts as a guard for the strategy. It’s optional to declare a valid? method, and if you don’t declare it, the strategy will always be run. If you do declare it though, the strategy will only be tried if #valid? evaluates to true.

The strategy above is reasoning that if there’s either a ‘username’ or a ‘password’ param, then the user is trying to login. If there’s only one of them, then the ‘User.authenticate’ call will fail, but it was still the desired (valid) strategy.

So your valid method:

def valid?
  params[:username] || params[:password]
end

It's returning false, so the authenticate! is never called. params is a nested hash, so it should be params[:user][:username] instead of params[:username].

Changing your valid method to:

def valid?
  params[:user] && (params[:user][:username] || params[:user][:password])
end

will return true and cause the authenticate! method to be called.



回答2:

I have run into a similar problem. After a short session of debugging I found out the reason. My user was not confirmed, so after initial successful signing in with my strategy, he was logged out by one of the following modules which is confirmable module :)

Btw, the easiest way to debug rails application is to use following code:

require 'ruby-debug'
Debugger.wait_connection = true
Debugger.start_remote
debugger

and then rdebug -c from terminal.