4

I know I am not suppose to do this, but I dont know how else should I do this. I want to use different database based on which user loged in. So I thought best way would be if I set up a session variable on first user login...

this is how it looks like:

class Stuff < ActiveRecord::Base
    establish_connection(
        :adapter  => "mysql2",
        :host     => "127.0.0.1",
        :username => session["dbuser"],
        :password => session["dbuserpass"],
        :database => session["dbname"])

and this of course does not work. Does anyone know how to do this? Thank you.

6
  • 2
    Rule one, do NOT give a user "root" access. Just don't. Rule two, don't base the connection on something the user can change on their machine, such as a session cookie or variable. User's are so darned ingenious there's no telling what they'll do. Commented Feb 5, 2013 at 0:40
  • this is just an example, each user will have its db user premissions... Commented Feb 5, 2013 at 0:42
  • 1
    Then please show that in your sample code, or explain it in your question. Commented Feb 5, 2013 at 0:43
  • ok, I have modified it, this way I will set 3 session variables for user, and then he will connect only to that specified db... Commented Feb 5, 2013 at 0:47
  • Instead of sessions, why not use User's record to switch the databases? I assume they have to log in anyway... This at least takes the session idea off the table. Commented Feb 17, 2013 at 3:15

4 Answers 4

3

You can rewrite your method as:

class Stuff < ActiveRecord::Base
 def establish_connection_user(user, pass, database)
   establish_connection(
    :adapter  => "mysql2",
    :host     => "127.0.0.1",
    :username => user,
    :password => pass,
    :database => database)
  end
end

and in your controller:

  class StuffController < ApplicationController
    def login #example 
      stuff = Stuff.new
      stuff.establish_connection_user(
        session[:dbuser], 
        session[:dbuserpass],
        session[:dbname]) 
    end

This way you also encapsulate it and make the details less obvious. I suggest you also encrypt your cookie so you don't have the credentials so exposed. You can take an idea from this answer:

Storing an encrypted cookie with Rails

Sign up to request clarification or add additional context in comments.

Comments

1

You can select the database in your model like this:

establish_connection "db_name_#{session[:something]}"

This way your model know which database to pull/push data.

Look at this: http://m.onkey.org/how-to-access-session-cookies-params-request-in-model

3 Comments

hm...I still get undefined local variable or method `session'
Sorry, i modified the answer. It's not good to do this, but it is an valid experiment.
thank you, but it does not seems to work...when I access establish_connection "#{session[:dbname]}" I got You have a nil object when you didn't expect it!...but I am sure session[:dbname] is set...
0

As hinted by kwon in the comments on the original question, I would approach it through using the session only to retain the identity of the user. I would then pull the desired database connection from logic in a model and a central database (the default rails DB) persisting user details and user connection information.

Start with a modification to your user model (assuming that your user has a model that is persisted in a central database)

  • add an attribute to the user representing the data to be used
  • in your application controller set the user in a before_filter, based on the session key
  • initialize your Stuff model with a user argument

You can then lookup your database connection based on the database.yml. Or if you have one database per user and you need this to be dynamic, create a second model (in the central database) representing the database connection with a foreign key onto the user model.

The following is a bunch of code that may or may not work in reality, but hopefully gives you a template for getting started.

class ApplicationController < ActionController::Base
  before_filter :set_user

  def set_user
    begin
      @user = UserProfile.find(session[:usernumber]) if session[:usernumber]        
    rescue
      logger.warn "Possible error in set_user. Resetting session: #{$!}"
      @user=nil
      session[:usernumber]=nil
      reset_session
    end
  end

end

class StuffController < ApplicationController
  def show
    @stuff = Stuff.user_get(@user, params[:id])
  end
end

class Stuff < ActiveRecord::Base
  # This would be better moved to a module to reuse across models
  def self.establish_connection_user(user)
    establish_connection(user.connection_hash)
  end
  def establish_connection_user(user)
    establish_connection(user.connection_hash)
  end

  def self.user_get user, item_id        
    establish_connection_user(user)
    find(id)
  end

  def self.user_where user, *query_args        
    establish_connection_user(user)
    where(query_args)
  end
  # Even better than replicating 'where', create model methods 
  # that are more representative of your desired functionality
end

class User  < ActiveRecord::Base
  has_one :user_connection
  def connection_hash
    uc = self.user_connection
    {:database=>uc.db, :password=>uc.pass, :user=>uc.username, :host=>uc.dbhost, :adapter=>uc.adapter}
  end
  # User probably contains other user-facing details
end

Comments

0

If you have the option of using PostgreSQL you can use the Schemas feature of PostgreSQL to effectively have separate table namespaces (schemas) for each user. The benefit here is you are still connected to the same database (thus avoiding hacking up the rails API), but you get the same benefits of multiple DBs in terms of database separation.

If you have a RailsCasts Pro subscription ($9/mo) Ryan Bates has an excellent video on the subject: http://railscasts.com/episodes/389-multitenancy-with-postgresql

Jerod Santo also did a great write up on his blog: http://blog.jerodsanto.net/2011/07/building-multi-tenant-rails-apps-with-postgresql-schemas/

In both examples they use subdomains to switch between tenants/schemas but you could easily link it to a user record.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.