Как добавить аутентификацию перед фильтрованием в rails 3 приложения с устройством для регистрации и входа в систему? - PullRequest
0 голосов
/ 10 ноября 2011

Учитывая, что я на Rails 3.1, Ruby 1.9.2 со стандартной настройкой устройства для входа пользователя в систему по имени и паролю (например, аналогично https://github.com/RailsApps/rails3-devise-rspec-cucumber):

Мне нужно добавить слой, который проверяет подлинность опубликованного имени пользователя и пароля по отношению к внешней службе до создания нового пользователя (регистрация) или при входе пользователя.

(FWIW, в дальнейшем я планирую использовать стратегию / гем https://github.com/chicks/devise_aes_encryptable для шифрования секретного пароля и при входе в систему с локальным паролем расшифровывать пароль удаленной службы, выполнить аутентификацию, затем продолжить вход в систему, то есть иметь два пароля, один зашифрованный в одну сторону, другой обратимый ... не спрашивайте почему, во всяком случае)

В lib / util / authenticate.rb у меня есть класс аутентификации, который возвращает логическое значение для этой службы, например

Util::Authenticate.authenticate(username,password)

Но я не могу понять, как добавить фильтр для проверки подлинности на его основе в публикации формы перед продолжением проверки подлинности (для регистрации или входа).

Что я пробовал:

У меня есть модель User, и я подумал поставить

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

в UserController, но это не сработало

Итак, я попытался открыть контроллер Devise Sessions, который не работал и не занимал его подклассы (например, в README),

class User::SessionsController < Devise::SessionsController
  # something
end

# in config/routes.rb
devise_for :users, :controllers => { :sessions => "users/sessions" }

и не наследовать контроллер Devise Registrations (например, http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/) и добавлять фильтр before_filter (такой же, как выше).

# in app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
  before_filter :check_permissions, :only => [: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

Я думаю, что мне нужно сделать это в контроллере, потому что как только параметры попадут в модель, у меня не будет незашифрованного пароля для отправки во внешнюю службу.

Я рассмотрел некоторые расширения для таких идей, как https://raw.github.com/nbudin/devise_cas_authenticatable/master/lib/devise_cas_authenticatable/strategy.rb например,

требуется 'разработка / стратегия / основа'

module Devise
  module Strategies
    class CasAuthenticatable < Base
      # True if the mapping supports authenticate_with_cas_ticket.
      def valid?
        mapping.to.respond_to?(:authenticate_with_cas_ticket) && params[:ticket]
      end

      # Try to authenticate a user using the CAS ticket passed in params.
      # If the ticket is valid and the model's authenticate_with_cas_ticket method
      # returns a user, then return success.  If the ticket is invalid, then either
      # fail (if we're just returning from the CAS server, based on the referrer)
      # or attempt to redirect to the CAS server's login URL.
      def authenticate!
        ticket = read_ticket(params)
        if ticket
          if resource = mapping.to.authenticate_with_cas_ticket(ticket)
            # Store the ticket in the session for later usage
            if ::Devise.cas_enable_single_sign_out
              session['cas_last_valid_ticket'] = ticket.ticket
              session['cas_last_valid_ticket_store'] = true
            end

            success!(resource)
          elsif ticket.is_valid?
            username = ticket.respond_to?(:user) ? ticket.user : ticket.response.user
            redirect!(::Devise.cas_unregistered_url(request.url, mapping), :username => 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)

и прочитайте о стратегиях аутентификации devise / warden, например, https://github.com/hassox/warden/wiki/Strategies но интересно, нужно ли мне на самом деле создать новую стратегию (и могу ли я выяснить, как это сделать)

РЕДАКТИРОВАТЬ, ВОЗМОЖНЫЕ РЕШЕНИЯ:

Мне нравится предложение Ално, и я попробую его, хотя он больше похож на обезьяну, чем на то, как предполагается использовать устройство / охранник

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

В качестве альтернативы я рассмотрел добавление стратегии аутентификации начальника, но сложно определить все движущиеся части, например, от Пользовательская стратегия аутентификации для устройства

initializers / authentication_strategy.rb: # это также может быть в initializers / devise.rb, нет?

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, e.g. 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 

добавить следующее в инициализаторы / 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

ИЛИ с Как добавить стратегию в 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?

Ответы [ 2 ]

1 голос
/ 10 ноября 2011

Если вы видите в devise source , вы найдете метод valid_password?, который принимает незашифрованный пароль, так что вы можете переопределить его для аутентификации по сравнению с некоторыми экстензальными услугами.

Что-то вроде:

def valid_password?(password)
  ExternalService.authenticate(email, password)
end
0 голосов
/ 10 ноября 2011

Вы должны вносить изменения в слой модели, а не в контроллер. Фактически, я бы посоветовал вам создать файл в /lib/whatever, который обрабатывает связь с внешней службой, а затем изменить вашу модель User, чтобы она проверяла внешнюю службу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...