2010-04-22 66 views
4

在Authlogic中,有沒有一種方法可以向身份驗證方法添加條件?我知道通過使用find_by_login_method我可以指定另一種方法來使用,但是當我使用這個我需要傳遞另一個參數,因爲find_by_login_method方法只傳遞被認爲是'login_field'的參數。Rails Authlogic身份驗證方法

我需要做的是檢查的東西,是地道的模型的關聯。這裏是我想用

# make sure that the user has access to the subdomain that they are 
    # attempting to login to, subdomains are company names 
    def self.find_by_email_and_company(email, company) 
    user = User.find_by_email(email) 

    companies = [] 
    user.brands.each do |b| 
     companies << b.company.id 
    end 

    user && companies.include?(company) 
    end 

的方法,但這種失敗,因爲事實上只有一個參數發送到find_by_email_and_company方法。

該公司實際上是子域,所以爲了得到它在這裏,我只是把它放在一個隱藏字段的形式(只有這樣我能想到得到它的模型)

是否有方法我可以重寫......?

使用下面我想出了工作如下答案:

用戶模型(User.rb)

def self.find_by_email_within_company(email) 
    # find the user 
    user = self.find_by_email(email) 

    # no need to continue if the email address is invalid 
    return false if user.nil? 

    # collect the subdomains the provided user has access to 
    company_subdomains = user.brands.map(&:company).map(&:subdomain) 

    # verify that the user has access to the current subdomain 
    company_subdomains.include?(Thread.current[:current_subdomain]) && user 
    end 

應用控制器

before_filter :set_subdomain 

    private 

    def set_subdomain 
     # helper that retreives the current subdomain 
     get_company 

     Thread.current[:current_subdomain] = @company.subdomain 
    end 

find_by_login_method :find_by_email_within_company 

我讀過有關使用Thread.current和衝突的命名空間的幾件事用戶會話模型(UserSession.rb)..這是爲我工作,但我非常賞金到期前聽到任何其他建議,否則一個很好的解決方案,以+100延Fahnenbruck :)

+0

您需要將「user && companies.include?(company)」換成「companies.include?(company)&& user」,因此User#find_by_email_and_company返回用戶模型而不是「true」或「false」陣列#包括哪些內容?方法 – jigfox 2010-04-26 19:12:22

+1

您對Thread.current可能有問題的權利。所以我用另一種方式更新了我的答案 – jigfox 2010-04-26 21:25:08

+0

多數民衆贊成在有趣的是,您鏈接到的帖子是我讀的帖子通知我Thread.current是不好的,感謝Jens的幫助! – Rabbott 2010-04-26 22:23:17

回答

2

Authlogic提供API爲處理子基於域的認證。

class User < ActiveRecord::Base 
    has_many :brands 
    has_many :companies, :through => :brands 
    acts_as_authentic 
end 

class Brand < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :company 
end 

class Company < ActiveRecord::Base 
    has_many :brands 
    has_many :users, :through => :brands 
    authenticates_many :user_sessions, :scope_cookies => true 
end 

會話控制器:

class UserSessionsController < ApplicationController  

    def create 
    @company = Company.find(params[:user_session][:company]) 
    @user_session = @company.user_sessions.new(params[:user_session]) 
    if @user_session.save 
    else 
    end  
    end 
end 

在另一方面

下面是使用當前的方法來解決這個問題的方式(我將使用第一種方法):

設置自定義數據 - 用於創建UserSession對象的散列的密鑰email。 AuthLogic會將此值傳遞給find_by_login方法。在find_by_login方法中訪問所需的值。

假設條件: 該子域id在表單中設置爲company

class UserSessionsController < ApplicationController  

    def create 
    attrs = params[:user_session].dup #make a copy 
    attrs[:email] = params[:user_session] # set custom data to :email key 

    @user_session = UserSession.new(attrs) 
    if @user_session.save 
    else 
    end  
    end 
end 

型號代碼

你的查找與給定的電子郵件和子域的用戶代碼可以簡化和優化如下:

class User < ActiveRecord::Base 
    def find_by_email params={} 
    # If invoked in the normal fashion then .. 
    return User.first(:conditions => {:email => params}) unless params.is_a?(Hash) 

    User.first(:joins => [:brands => :company}], 
    :conditions => ["users.email = ? AND companies.id = ?", 
         params[:email], params[:company]]) 
    end 
end 

編輯1

一旦用戶通過身份驗證,系統應提供對授權數據的訪問。

如果您維護同一個表中所有域的數據,那麼您必須按子域和經過身份驗證的用戶來確定數據的範圍。 假設您有Post型號,其中company_iduser_id列。當用戶登錄時,您想要顯示子域的用戶帖子。這是一個辦法範圍用戶的子域數據:

Posts.find_by_company_id_and_user_id(current_company, current_user) 

Posts.for_company_and_user(current_company, current_user) # named scope 

如果不這樣做範圍的數據,你會在你的系統中潛在的安全漏洞。

+0

將公司作爲一個表單域(隱藏我猜測)有多安全,因爲藉助Firebug,我可以顯示隱藏域並修改其內容。因此,如果用戶更改登錄表單域中的值,但是一旦登錄後,他們將獲得特定於該子域的信息,這可能會導致問題。 – Rabbott 2010-05-02 16:31:07

+0

即使用戶更改子域名,只要他在子域中擁有帳戶,就會被允許登錄。認證後的呼叫應確保用戶正在訪問授權內容(無論如何這是必需的)。 – 2010-05-02 16:34:24

+0

一旦登錄內容的範圍是子域,沒有大量的授權檢查,你不能總是檢查每個頁面加載的內容..所以,我可以改變公司的形式,我也有權訪問的公司,這將允許我登錄。然後,一旦登錄我的帳戶不再檢查每個網頁加載,以驗證我有權訪問的內容。 – Rabbott 2010-05-02 17:02:27

1

在你的lib文件夾添加文件與follwing內容:

class Class 
    def thread_local_accessor name, options = {} 
    m = Module.new 
    m.module_eval do 
     class_variable_set :"@@#{name}", Hash.new {|h,k| h[k] = options[:default] } 
    end 
    m.module_eval %{ 
     FINALIZER = lambda {|id| @@#{name}.delete id } 

     def #{name} 
     @@#{name}[Thread.current.object_id] 
     end 

     def #{name}=(val) 
     ObjectSpace.define_finalizer Thread.current, FINALIZER unless @@#{name}.has_key? Thread.current.object_id 
     @@#{name}[Thread.current.object_id] = val 
     end 
    } 

    class_eval do 
     include m 
     extend m 
    end 
    end 
end 

我發現這個here

然後在控制器這樣添加代碼:

class ApplicationController < ActionController 
    before_filter :set_subdomain 
    private 
    def set_subdomain 
    User.subdomain = request.subdomains[0] 
    end 
end 

現在你可以做以下在您的用戶模型(假設你的公司模式有一個方法叫子域名:

class User < ActiveRecord::Base 
    thread_local_accessor :subdomain, :default => nil 

    def self.find_by_email_within_company(email) 
    self.find_by_email(email) 
    company_subdomains = user.brands.map(&:company).map(&:subdomain) 
    company_subdomains.include?(self.subdomain) && user 
    end 
end 

,僅供參考:

companies = user.brands.map(&:company).map(&:subdomain) 

相同

companies = [] 
user.brands.each do |b| 
    companies << b.company.subdomain 
end 
+0

感謝.map($ :)技巧,我可以全面使用它,你能解釋一下嗎?或者只是在這裏打一個鏈接,我可以在它上面讀到..? – Rabbott 2010-04-26 22:34:30

+0

http://ruby-doc.org/core/classes/Array.html#M002189 .map(&:method_name)是.map {| o |的簡寫形式。 o.method_name} 我不確定這是Ruby,還是Rails核心擴展的一部分。所以我不知道它是否適用於純紅寶石 – jigfox 2010-04-26 23:09:48

+0

太好了,謝謝! - 我會接受你的解決方案,直到賞金結束,讓其他人也有機會作出迴應,因爲我接受後我不能改變它。 – Rabbott 2010-04-28 04:16:59

0

了Rails 3,您可以使用此解決辦法:

class UserSessionsController < ApplicationController 
... 
def create 
    @company = <# YourMethodToGetIt #> 
    session_hash = params[:user_session].dup 
    session_hash[:username] = { :login => params[:user_session][:username], :company => @company } 
    @user_session = UserSession.new(session_hash) 

    if @user_session.save 
     flash[:notice] = "Login successful!" 
     redirect_back_or_default dashboard_url 
    else 
     @user_session.username = params[:user_session][:username] 
     render :action => :new 
    end 

... 
end 

然後

class UserSession < Authlogic::Session::Base 
    find_by_login_method :find_by_custom_login 
end 

class User < ActiveRecord::Base 
... 
    def self.find_by_custom_login(hash) 
     if hash.is_a? Hash 
      return find_by_username_and_company_id(hash[:login], hash[:company].id) || 
       find_by_email_and_company_id(hash[:login], hash[:company].id) 
     else 
      raise Exception.new "Error. find_by_custom_login MUST be called with {:login => 'username', :company => <Company.object>}" 
     end 
    end 
... 
end 

這是相當簡單的和 「正確的」。我花了很多時間找出來,但它工作正常!