设计和使徒行动AsTenant不能很好地融合在一起

时间:2014-07-20 12:36:58

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

我正在使用ActsAsTenant,我在任何Devise路线(即任何Devise控制器)上都会收到以下错误。似乎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 ::错误:: 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

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

这是一则古老的文章,但是唯一一个询问这个确切问题的文章,没有真正的解决方案。 尝试使用没有子域的devise和act_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 / intializers / 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

答案 2 :(得分:0)

不幸的是,我必须要做的就是摆脱Devise并用Clearance替换它,这解决了第一轮问题,然后我摆脱了ActsAsTenant并写了我自己的小模块,包括设置默认范围如果没有设置租户,则抛出错误。总而言之,工作几个小时,但现在一切都变得简单,所以非常值得。

答案 3 :(得分:0)

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

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

父控制器是您的ApplicationController。实例化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