¿Cómo agregar autenticación antes de filtrar a las aplicaciones de Rails 3 con un dispositivo para que el usuario se registre e inicie sesión?

Dado que estoy en Rails 3.1, Ruby 1.9.2 con una configuración estándar para el dispositivo para registrar un usuario por nombre y contraseña (por ejemplo, similar a https://github.com/RailsApps/rails3-devise-rspec-cucucumber ):

Necesito agregar una capa que autentique un nombre de usuario y una contraseña publicados en un servicio externo antes de crear un nuevo usuario (registrarse) o cuando un usuario inicia sesión.

(FWIW, más adelante, planeo usar la estrategia / gem https://github.com/chicks/devise_aes_encryptable para cifrar la contraseña confidencial y, al iniciar sesión con una contraseña local, descifrar la contraseña del servicio remoto, autenticar , luego continúe con el inicio de sesión, es decir, tenga dos contraseñas, una cifrada de una manera, la otra reversible … no pregunte por qué, de todos modos)

En lib / util / authenticate.rb tengo una clase de autenticación que devuelve un booleano contra este servicio, por ejemplo

Util::Authenticate.authenticate(username,password) 

Sin embargo, no puedo descubrir cómo agregar un filtro para autenticarse contra él en la publicación del formulario antes de que la autenticación continúe (para registrarse o iniciar sesión).

Lo que he intentado:

Tengo un modelo de usuario y pensé en poner un

 before_filter :authenticate_against_my_service, :only => [:create, :new] 

en el UserController pero eso no funcionó

Entonces, intenté abrir el controlador de Sesiones de Devise, que no funcionó, ni lo hice subclasificando (por ejemplo, en el README),

 class User::SessionsController  { :sessions => "users/sessions" } 

ni subclasificar el controlador de Devise Registrations (por ejemplo, http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/ ) y agregar un before_filter (igual que encima).

 # in app/controllers/users/registrations_controller.rb class Users::RegistrationsController  [:new, :create, :cancel] skip_before_filter :require_no_authentication def check_permissions authorize! :create, resource end end # and in config/routes.rb root :to => "home#index" # replace "devise_for :users" with the below devise_for :users, :controllers => { :registrations => "users/registrations" } # other related code devise_for :users do get 'logout' => 'devise/sessions#destroy' end # resources :users, :only => :show MUST be below devise_for :users resources :users, :only => :show 

Creo que tengo que hacer esto en el controlador porque una vez que los parámetros lleguen al modelo, no tendré una contraseña sin cifrar para enviar al servicio externo.

Miré algunas extensiones para ideas como https://raw.github.com/nbudin/devise_cas_authenticatable/master/lib/devise_cas_authenticatable/strategy.rb por ejemplo

requiere ‘diseñar / estrategias / base’

 module Devise module Strategies class CasAuthenticatable  username) #fail!("The user #{ticket.response.user} is not registered with this site. Please use a different account.") else fail!(:invalid) end else fail!(:invalid) end end protected def read_ticket(params) #snip end end end end Warden::Strategies.add(:cas_authenticatable, Devise::Strategies::CasAuthenticatable) 

y lea sobre las estrategias de autenticación de dispositivos / guardias, por ejemplo, https://github.com/hassox/warden/wiki/Strategies, pero me pregunto si realmente debo crear una nueva estrategia (y si puedo averiguar cómo hacerlo)

EDITAR, POSIBLES SOLUCIONES:

Me gusta la sugerencia de alno y lo intentaré, aunque parece más un parche de monos que la forma en que se debe usar el invento / guardián

 module Devise::Models::DatabaseAuthenticatable alias_method :original_valid_password?, :valid_password? def valid_password? if Util::Authenticate.authenticate(username,password) original_valid_password? else false end end end 

alternativamente, he buscado agregar una estrategia de autenticación de guardia, pero es difícil entender todas las partes móviles, por ejemplo, de la estrategia de autenticación personalizada para dispositivos

initializers / authentication_strategy.rb: # esto también podría estar en initializers / devise.rb, ¿no?

 Warden::Strategies.add(:custom_external_authentication) do def valid? # code here to check whether to try and authenticate using this strategy; return true # always use the strategy as only user's authenticate end def authenticate! # code here for doing authentication; if successful, call success! # whatever you've authenticated, eg user; if fail, call fail! if Util::Authenticate.authenticate(username, password) success!(true) # I don't think I want to return a user, as I'll let database authenticatable handle the rest of the authentication # I don't think I'm using success! correctly here: https://github.com/hassox/warden/wiki/Strategies # success!(User.find(someid)) else fail!("Username and password not valid for external service. Please ensure they are valid and try again.") end end end 

agregar lo siguiente a initializers / devise.rb

 Devise.setup do |config| config.warden do |manager| manager.default_strategies.unshift :custom_external_authentication # will this check before or after database authentication? I want before, I think end end 

O desde ¿Cómo agrego una estrategia a Devise?

 class ExternalServiceStrategy def valid? true # always use this end def authenticate! # external boolean service call end end Warden::Strategies.add(:database_authenticatable, ExternalServiceStrategy) # will this work before the db authentication? 

Si ve en la fuente del valid_password? , encontrará una valid_password? Método, que acepta una contraseña no encriptada, por lo que puede anularla para autenticar frente a algún servicio externo.

Algo como:

 def valid_password?(password) ExternalService.authenticate(email, password) end 

Debería realizar los cambios en la capa del modelo, no en el controlador. De hecho, le aconsejaría que cree un archivo en /lib/whatever que /lib/whatever que maneje hablar con el servicio externo, y luego modifique su modelo de User para que compruebe el servicio externo.