【问题标题】:Devise and ActsAsTenant not playing nicely together设计和 ActsAsTenant 不能很好地配合
【发布时间】:2014-07-20 12:36:57
【问题描述】:

我正在使用 ActsAsTenant,并且在任何设计路线(即任何设计控制器)上都不断收到以下错误。似乎 Devise 试图在设置租户之前获取 current_user 或与获取用户有关的东西,因此 ActsAsTenant 然后引发错误。我尝试使用 prepend_before_action 来设置租户,但没有奏效。

class ApplicationController < ActionController::Base  
    protect_from_forgery with: :exception

    prepend_before_action :secure_app
    before_action :authenticate_user!

    private

    def secure_app
        self.class.set_current_tenant_by_subdomain_or_domain
    end
end

如何确保在 Devise 开始查找 current_user 之前设置了租户?

ActsAsTenant::Errors::NoTenantSet at /edit ActsAsTenant::Errors::NoTenantSet

block in User.acts_as_tenant
() home/lee/.rvm/gems/ruby-2.1.1/bundler/gems/acts_as_tenant-1b7d146d750b/lib/acts_as_tenant/model_extensions.rb, line 54
block (3 levels) in User.build_default_scope
activerecord (4.1.4) lib/active_record/scoping/default.rb, line 103
User::ActiveRecord_Relation#scoping
activerecord (4.1.4) lib/active_record/relation.rb, line 285
block (2 levels) in User.build_default_scope
activerecord (4.1.4) lib/active_record/scoping/default.rb, line 103
block in User.build_default_scope
activerecord (4.1.4) lib/active_record/scoping/default.rb, line 102
User.evaluate_default_scope
activerecord (4.1.4) lib/active_record/scoping/default.rb, line 125
User.build_default_scope
activerecord (4.1.4) lib/active_record/scoping/default.rb, line 101
User.default_scoped
activerecord (4.1.4) lib/active_record/scoping/named.rb, line 33
User.all
activerecord (4.1.4) lib/active_record/scoping/named.rb, line 28
User.where
activerecord (4.1.4) lib/active_record/querying.rb, line 10
OrmAdapter::ActiveRecord#get
orm_adapter (0.5.0) lib/orm_adapter/adapters/active_record.rb, line 17
User.serialize_from_session
devise (3.2.4) lib/devise/models/authenticatable.rb, line 208
block (2 levels) in Warden::SessionSerializer#user_deserialize
devise (3.2.4) lib/devise.rb, line 462
Warden::SessionSerializer#fetch
warden (1.2.3) lib/warden/session_serializer.rb, line 34
Warden::Proxy#user
warden (1.2.3) lib/warden/proxy.rb, line 212
Warden::Proxy#_perform_authentication
warden (1.2.3) lib/warden/proxy.rb, line 318
Warden::Proxy#authenticate!
warden (1.2.3) lib/warden/proxy.rb, line 127
RegistrationsController#authenticate_user!
devise (3.2.4) lib/devise/controllers/helpers.rb, line 50
RegistrationsController#authenticate_scope!
devise (3.2.4) app/controllers/devise/registrations_controller.rb, line 124
block in ActiveSupport::Callbacks::Callback#make_lambda
activesupport (4.1.4) lib/active_support/callbacks.rb, line 424
block in ActiveSupport::Callbacks::Filters::Before.halting_and_conditional
activesupport (4.1.4) lib/active_support/callbacks.rb, line 143
RegistrationsController#run_callbacks
activesupport (4.1.4) lib/active_support/callbacks.rb, line 86
RegistrationsController#process_action
actionpack (4.1.4) lib/abstract_controller/callbacks.rb, line 19
RegistrationsController#process_action
actionpack (4.1.4) lib/action_controller/metal/rescue.rb, line 29
block in RegistrationsController#process_action
actionpack (4.1.4) lib/action_controller/metal/instrumentation.rb, line 31

【问题讨论】:

    标签: ruby-on-rails devise acts-as-tenant


    【解决方案1】:

    这是一篇旧帖子,但唯一一个询问这个确切问题的帖子,并且没有真正的解决方案。 尝试在没有子域的情况下使用设计和acts_as_tenant 时出现错误。我想根据用户查找租户。这是我得到的错误:ActsAsTenant::Errors::NoTenantSet。

    有解决方案here,但这些对我不起作用。

    我找到的解决方案是重写 Devise 用于扩展 User 模型的一些方法,以便这些方法使用无范围的,如 here 所示。 使用设计 4.6.2、acts_as_tenant 0.4.3、Rails 5.2.3

    app/models/devise_overrides.rb

    module DeviseOverrides
      def find_for_authentication(conditions)
        unscoped { super(conditions) }
      end
    
      def serialize_from_session(key, salt)
        unscoped { super(key, salt) }
      end
    
      def send_reset_password_instructions(attributes={})
        unscoped { super(attributes) }
      end
    
      def reset_password_by_token(attributes={})
        unscoped { super(attributes) }
      end
    
      def find_recoverable_or_initialize_with_errors(required_attributes, attributes, error=:invalid)
        unscoped { super(required_attributes, attributes, error) }
      end
    
      def send_confirmation_instructions(attributes={})
        unscoped { super(attributes) }
      end
    
      def confirm_by_token(confirmation_token)
        unscoped { super(confirmation_token) }
      end
    end
    

    然后在 app/models/user.rb:

    class User < ApplicationRecord
      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
      devise :invitable, :database_authenticatable, :registerable, :confirmable,
             :recoverable, :rememberable, :validatable
    
      belongs_to :account
      accepts_nested_attributes_for :account
    
      acts_as_tenant(:account)
      extend DeviseOverrides
    end
    

    现在你可以添加一个 config/initializers/acts_as_tenant.rb 并且一切都会继续工作。

    ActsAsTenant.configure do |config|
      config.require_tenant = true
    end
    

    我的 app/controllers/application_controller.rb 看起来像这样:

    class ApplicationController < ActionController::Base
      set_current_tenant_through_filter
      before_action :set_tenant
      before_action :authenticate_user!
    
      private
    
      def set_tenant
        current_account = current_user.account
        set_current_tenant(current_account)
      end
    end
    

    【讨论】:

    • 我不知道如何确认上面 app/models/devise_overrides.rb 中的方法是正确的还是我们唯一需要覆盖的方法。该解决方案有效,但我想更深入地了解为什么它有效。如果您知道,请发表评论。
    【解决方案2】:

    可以解决这个问题,您可以在此错误报告中看到它:https://github.com/ErwinM/acts_as_tenant/issues/49#issuecomment-77142527

    【讨论】:

      【解决方案3】:

      好吧,不幸的是,我要做的是摆脱 Devise 并用 Clearance 替换它,这解决了第一轮问题,接下来我摆脱了 ActsAsTenant 并编写了我自己的小模块,其中包含设置默认范围如果没有设置租户,则抛出错误。总而言之,工作了几个小时,但现在一切都简单多了,所以值得

      【讨论】:

        【解决方案4】:

        即使在将 ApplicationController 更改为使用 prepend_before_action 之后,您仍然收到 ActsAsTenant::Errors::NoTenantSet 错误的原因是因为 DeviseController 被声明为

        class DeviseController < Devise.parent_controller.constantize
        ...
        prepend_before_action :assert_is_devise_resource!
        ...
        end
        

        父控制器是您的应用程序控制器。当 DeviseController 被实例化时,它首先运行 ApplicationController 中的所有代码,这些代码在你的 secure_app 方法之前(放在回调链的当前前面)。只有在那之后才能实例化子类(DeviseController),它预先设置为assert_is_devise_resource!回调(现在将它放在您的 secure_app 方法之前)。

        如果您想在多个租户中拥有相同的用户(电子邮件或您使用的任何唯一外部密钥),则上述无范围解决方案不起作用。你可以通过继承 DeviseControllers 来解决这个问题,这样你就可以预先添加你的 secure_app 方法。

        routes.rb
        devise_for :users, only: %i[session], path: 'users',
                     path_names: {sign_in: 'login', sign_out: 'logout'},
                     controllers: {
                         sessions: 'users/sessions'
                     }
        
        module Users
          class SessionsController < Devise::SessionsController
        
            # You can access secure_app because SessionsController inherits from your ApplicationController
            prepend_before_action :secure_app
        
            # GET /users/sign_in
            def new
              super
            end
        
            # DELETE /users/sign_out
            def destroy
              super
            end
        
          end
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-07-31
          • 2011-04-15
          • 1970-01-01
          • 1970-01-01
          • 2010-09-28
          • 1970-01-01
          • 2016-12-26
          相关资源
          最近更新 更多