使用Devise和devise_cas_authenticatable进行无限重定向

时间:2018-03-05 12:04:24

标签: ruby-on-rails devise cas

我有一个Rails 5.2应用程序,它使用Devise 4.4.1和devise_ldap_authenticatable进行身份验证。我尝试将其切换为devise_cas_authenticatable,现在在CAS登录页面输入我的凭据后进入重定向循环。

应用程序,CAS服务器(rubycas-server),LDAP和数据库都通过Docker Compose作为Docker容器运行。 CAS服务器配置为使用HTTP进行开发。

据我所知,我的应用程序在登录后CAS返回时没有设置会话,但我不明白为什么。非常感谢收到一些调试指针。

以下是每次重定向重复的日志部分:

app_1       | Started GET "/" for 172.18.0.1 at 2018-03-05 11:10:33 +0000
app_1       |    (0.3ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
app_1       |   ↳ /usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/log_subscriber.rb:98
app_1       |    (0.3ms)  SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC
app_1       |   ↳ /usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/log_subscriber.rb:98
app_1       | Processing by HomeController#index as HTML
app_1       | ### Params: <ActionController::Parameters {"controller"=>"home", "action"=>"index"} permitted: false>
app_1       | ### Referrer:
app_1       | ### Session: nil
app_1       | Completed 401 Unauthorized in 11ms (ActiveRecord: 0.0ms)
app_1       |
app_1       |
app_1       | Started GET "/users/sign_in" for 172.18.0.1 at 2018-03-05 11:10:34 +0000
app_1       | Processing by Devise::CasSessionsController#new as HTML
app_1       | ### Params: <ActionController::Parameters {"controller"=>"devise/cas_sessions", "action"=>"new"} permitted: false>
app_1       | ### Referrer:
app_1       | ### Session: nil
app_1       | Redirected to http://cas:8080/cas/login?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice
app_1       | Completed 302 Found in 6ms (ActiveRecord: 0.0ms)
app_1       |
app_1       |
cas_1       | 172.18.0.1 - - [05/Mar/2018:11:10:34 UTC] "GET /cas/login?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice HTTP/1.1" 200 2517
cas_1       | - -> /cas/login?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice
cas_1       | 172.18.0.1 - - [05/Mar/2018:11:10:38 UTC] "POST /cas/login HTTP/1.1" 303 0
cas_1       | http://cas:8080/cas/login?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice -> /cas/login
app_1       | Started GET "/users/service?ticket=ST-1520248238rDxT--HBG7GmKRhNydV" for 172.18.0.1 at 2018-03-05 11:10:38 +0000
app_1       | Processing by Devise::CasSessionsController#service as HTML
app_1       |   Parameters: {"ticket"=>"ST-1520248238rDxT--HBG7GmKRhNydV"}
app_1       | ### Params: <ActionController::Parameters {"ticket"=>"ST-1520248238rDxT--HBG7GmKRhNydV", "controller"=>"devise/cas_sessions", "action"=>"service"} permitted: false>
app_1       | ### Referrer: http://cas:8080/cas/login?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice
cas_1       | railsapp_app_1.railsapp_rails-app-test-net - - [05/Mar/2018:11:10:38 UTC] "GET /cas/proxyValidate?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice&ticket=ST-1520248238rDxT--HBG7GmKRhNydV HTTP/1.1" 200 263
cas_1       | - -> /cas/proxyValidate?service=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fservice&ticket=ST-1520248238rDxT--HBG7GmKRhNydV
app_1       | Using conditions {username => student_sd} to find the User
app_1       |   User Load (0.6ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'student_sd' ORDER BY `users`.`id` ASC LIMIT 1
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |    (0.4ms)  BEGIN
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |   User Exists (0.5ms)  SELECT  1 AS one FROM `users` WHERE `users`.`username` = 'student_sd' LIMIT 1
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |   User Exists (0.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |    (0.4ms)  ROLLBACK
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |    (0.3ms)  BEGIN
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |   User Exists (0.3ms)  SELECT  1 AS one FROM `users` WHERE `users`.`username` = 'student_sd' LIMIT 1
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |   User Exists (0.3ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       |    (0.3ms)  ROLLBACK
app_1       |   ↳ app/controllers/application_controller.rb:44
app_1       | ### Session: {}
app_1       | Redirected to http://localhost:3000/
app_1       | Completed 302 Found in 90ms (ActiveRecord: 6.7ms)

这是我的ApplicationController:

class ApplicationController < ActionController::Base
  rescue_from ActionController::RoutingError do
    redirect_to root_path, alert: 'Page not found'
  end

  protect_from_forgery with: :exception

  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :do_authenticate!

  def scoped_user
    return nil if scoped_username.blank?

    @_scoped_user ||= User.includes(:courses).find_by(username: scoped_username)
  end
  helper_method :scoped_user

  def select_for_admin(option_of_the_proletariat, admin_option)
    if admin_mode?
      admin_option
    else
      option_of_the_proletariat
    end
  end
  helper_method :select_for_admin

  def admin_mode?
    user_signed_in? && (scoped_user != current_user)
  end
  helper_method :admin_mode?

  protected

    def do_authenticate!
      puts "### Params: #{params.inspect}" # debug
      puts "### Referrer: #{request.referer}"
      puts "### Session: #{user_session.inspect}"
      authenticate_user!
    end

    def allow_admin_only
      puts "allow_admin_only" # debug
      block_unauthorised! unless current_user.administrator?
    end

    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:sign_in, keys: [:username])
    end

    def block_unauthorised!
      puts "block_unauthorised!" # debug
      raise ActionController::RoutingError.new('Not found')
    end

    def scoped_username
      params[:username]
    end
end

如果它有用,我的用户模型:

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :cas_authenticatable, :trackable, :rememberable

  has_many :memberships
  has_many :courses, through: :memberships
  has_many :images, through: :memberships

  before_create :fetch_user_attributes

  validates :username, presence: true, uniqueness: { case_sensitive: false }
  validates :name, presence: true
  validates :email, presence: true, uniqueness: { case_sensitive: false }
  validates :department, presence: true
  validates :employee_type, presence: true

  scope :search, ->(query) {
    where('username LIKE ?', "%#{query}%").or(where('name LIKE ?', "%#{query}%"))
  }

  def member_of?(course)
    courses.include?(course)
  end

  def owned_tag_names
    ::Gutentag::Tag
      .joins(:taggings)
      .joins('join images on images.id = gutentag_taggings.taggable_id')
      .joins('join memberships on images.membership_id = memberships.id')
      .where('memberships.user_id = ?', self.id)
      .distinct
      .pluck(:name)
  end

  # "hack for remember_token" from devise_ldap_authenticatable wiki
  def authenticatable_salt
    Digest::SHA1.hexdigest(username)[0, 29]
  end

  def to_param
    username
  end

  def fetch_user_attributes
    # hard code values for testing
    raise '### Tried to fetch attributes'
    assign_attributes({
      email: "user-#{('a'..'z').to_a.shuffle[0,8].join}@example.ac.uk",
      name: 'User Person',
      department: 'DEN',
      employee_type: 'student'
    })
    # {
    #   email: Devise::LDAP::Adapter.get_ldap_param(username, 'mail').first,
    #   name: Devise::LDAP::Adapter.get_ldap_param(username, 'cn').first,
    #   department: Devise::LDAP::Adapter.get_ldap_param(username, 'ou').first,
    #   employee_type: Devise::LDAP::Adapter.get_ldap_param(username, 'employeeType').first
    # }
  end
end

1 个答案:

答案 0 :(得分:0)

devise_ldap_authenticatable调用resource.save时出现问题。验证在 before_create回调之前运行,并且这些验证错误无声地阻止用户被创建并因此进行身份验证。电子邮件字段中的唯一性验证是日志通过电子邮件显示SELECT的原因。

解决方案是改变

before_create :fetch_user_attributes

before_validation :fetch_user_attributes, on: :create

请参阅this issue