Rails Authentication Authentication Method

Is there a way inside Authlogic that I can add conditions for an authentication method? I know, using find_by_login_method , I can specify a different method to use, but when I use it, I need to pass another parameter, because the find_by_login_method method skips only the parameter that is considered to be "login_field".

What I need to do is check something that is an association of an authentic model. Here is the method I want to use

# 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 

But this fails because only one parameter is sent to the find_by_email_and_company method.

The company is actually a subdomain, so to get it here, I just put it in a hidden field in the form (only the way I thought to get it in the model)

Is there a way that I can somehow override ..?

Using the answer below, I came up with the following that worked:

User Model (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 

Application controller

  before_filter :set_subdomain private def set_subdomain # helper that retreives the current subdomain get_company Thread.current[:current_subdomain] = @company.subdomain end 

User Session Model (UserSession.rb)

 find_by_login_method :find_by_email_within_company 

I read a few things about using Thread.current and conflicting namespaces. This is a great solution that worked for me, but I would like to hear any other suggestions before the expiration date, otherwise +100 to Jens Fahnenbruck :)

+4
source share
3 answers

Authlogic provides an API for working with subdomain-based authentication.

 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 

Session Controller:

 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 

On the other hand

Here is a way to solve the problem using your current approach (I would use the first approach):

Set user data - to the email hash key used to create the UserSession object. AuthLogic will pass this value to the find_by_login method. In the find_by_login method, select the desired values.

Assumption: The subdomain identifier is set in the field named company in the form.

 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 

Model code

Your code to search for a user with a given email address and subdomain can be simplified and optimized as follows:

 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 

Change 1

After user authentication, the system should provide access to authorized data.

If you store data for all domains in the same table, you must have data for the subdomain and the authenticated user. Suppose you have a Post model with company_id and user_id . When a user logs in, you want to display user messages for the subdomain. This is one way to capture user data for a subdomain:

 Posts.find_by_company_id_and_user_id(current_company, current_user) Posts.for_company_and_user(current_company, current_user) # named scope 

If you do not cover the data, you will have potential holes in your system.

+2
source

In your lib folder, add a file with the following contents:

 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 

I found here here

Then add the code to the controller as follows:

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

And now you can do the following in your user model (if your company model has a subdomain method:

 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 

And FYI:

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

coincides with

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

With rails 3 you can use this workaround:

 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 

Then

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

and

 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 

This is pretty simple and "correct." I take a lot of time from me to find out, but it works great!

0
source

Source: https://habr.com/ru/post/1307743/


All Articles