require 'active_record' require 'unix_crypt' require 'bcrypt' class CASinoCore::Authenticator::ActiveRecord class AuthDatabase < ::ActiveRecord::Base self.abstract_class = true end # @param [Hash] options def initialize(options) @options = options eval <<-END class #{self.class.to_s}::#{@options[:table].classify} < AuthDatabase end END @model = "#{self.class.to_s}::#{@options[:table].classify}".constantize @model.establish_connection @options[:connection] end def validate(username, password) user = @model.send("find_by_#{@options[:username_column]}!", username) password_from_database = user.send(@options[:password_column]) if valid_password?(password, password_from_database) { username: user.send(@options[:username_column]), extra_attributes: extra_attributes(user) } else false end rescue ActiveRecord::RecordNotFound false end private def valid_password?(password, password_from_database) magic = password_from_database.split('$')[1] case magic when /\A2a?\z/ valid_password_with_bcrypt?(password, password_from_database) else valid_password_with_unix_crypt?(password, password_from_database) end end def valid_password_with_bcrypt?(password, password_from_database) BCrypt::Password.new(password_from_database) == password end def valid_password_with_unix_crypt?(password, password_from_database) UnixCrypt.valid?(password, password_from_database) end def extra_attributes(user) attributes = {} @options[:extra_attributes].each do |attribute_name, database_column| attributes[attribute_name] = user.send(database_column) end attributes end end