2009-10-21 12 views
13

Sto per iniziare a scrivere un'applicazione Rails che permetterà ai clienti di avere un sottodominio separato per il loro accesso alla nostra applicazione. Pensando dal punto di vista della sicurezza dei dati, sarebbe bello se l'accesso di ogni cliente fosse veramente limitato al loro database, in questo modo, se c'è un bug nel codice di produzione, sarebbero in grado di accedere al proprio database e non a quello di qualsiasi altro clienti.Rails - Database separato per sottodominio

Conosco il codice che spiega come fare ciò che voglio, ma mi chiedevo se esistesse una soluzione più semplice che potrei mancare. Come faresti per proteggere i dati dei clienti in modo che, in caso di bug o di minaccia da parte di hacker, i loro dati siano meno esposti?

risposta

20

Ecco il codice che uso per questo problema:

application_controller.rb

before_filter :set_database 

helper_method :current_website 

# I use the entire domain, just change to find_by_subdomain and pass only the subdomain 
def current_website  
    @website ||= Website.find_by_domain(request.host) 
end 

def set_database 
    current_website.use_database 
end 

# Bonus - add view_path 
def set_paths 
    self.prepend_view_path current_website.view_path unless current_website.view_path.blank? 
end 

Website.rb

def use_database 
    ActiveRecord::Base.establish_connection(website_connection) 
end 

# Revert back to the shared database 
def revert_database 
    ActiveRecord::Base.establish_connection(default_connection) 
end 

private 

# Regular database.yml configuration hash 
def default_connection 
    @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup 
end 

# Return regular connection hash but with database name changed 
# The database name is a attribute (column in the database) 
def website_connection 
    default_connection.dup.update(:database => database_name) 
end 

Spero che questo aiuti!

+0

+1 - Sicuramente molto utile. Puoi darmi un'idea del successo della performance (se presente) con questo approccio? –

+0

Mi spiace di non avere un benchmark per questo. Ho solo questo lavoro su una beta privata al momento. – Kris

+0

Qualcuno ha qualche ipotesi sul fatto che questo richiederà un impatto negativo sulle prestazioni o no? Mi piacerebbe fare qualcosa di simile per un sito di produzione. – NotDan

0

In cima alla mia testa è possibile eseguire una nuova istanza del server per ogni sottodominio utilizzando un ambiente diverso.

Ma questo non si ridimensionerà molto bene.

Tuttavia i primi google hits for multiple rails databases presentano alcuni nuovi suggerimenti. Mettere insieme le informazioni in questi collegamenti fornisce questa soluzione interamente non testata per una singola istanza del server.

È necessario aggiungere una voce di database per ogni sottodominio nel database.yml. Quindi aggiungi un before_filter al controller dell'applicazione

Aggiornamento! L'esempio ricarica dinamicamente le configurazioni del database. Sfortunatamente non c'è un buon modo per rendere i binari dell'aggiornamento senza compromettere gli interni del server. Quindi la configurazione del database dovrà essere ricaricata su ogni richiesta.

Questo esempio presuppone che le voci del database in databases.yml abbiano un nome dopo i sottodomini.

config/database.yml

login: &login 
    adapter: mysql 
    username: rails 
    password: IamAStrongPassword! 
    host: localhost 

production: 
    <<: *login 
    database: mysite_www 

subdomain1: 
    <<: *login 
    database: mysite_subdomain1 

subdomain2: 
    <<: *login 
    database: mysite_subdomain2 
... 

app/controllers/application_controller.rb richiedono 'erb' before_filter: switch_db_connection

def switch_db_connection 
    subdomain = request.subdomains.first 
    ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(Rails.configuration.database_configuration_file)).result) 
    ActiveRecord::Base.establish_connection("mysite_#{subdomain}") 
end 

Come ho detto è completamente testato. Ma non prevedo alcun problema importante. Se non funziona, spero che ti metta sulla buona strada.

+1

Il problema con questo è che quando si aggiunge un nuovo sottodominio è necessario riavviare tutte le istanze del server. Se stai aggiungendo molti sottodomini in una sola volta, questo significa alcuni tempi di inattività. –

+1

Creato l'esempio dinamico in una singola istanza del server. È comunque non testato. – EmFi

0

È venuto fuori che ho appena chiesto uno really similar question ma un po 'più avanti nello sviluppo: ho incluso tre idee su come utilizzare in modo sicuro un singolo database.

+0

Grazie per il link. Mi dà alcune idee, ma sembra che non ci sia ancora una buona soluzione per questo problema. –

4

ho trovato una soluzione diversa che funziona un po 'più facile, ma rende l'ipotesi di avere un database per ogni sottodominio:

application_controller.rb

before_filter :subdomain_change_database 

def subdomain_change_database 
    if request.subdomain.present? && request.subdomain != "www" 
    # 'SOME_PREFIX_' + is optional, but would make DBs easier to delineate 
    ActiveRecord::Base.establish_connection(website_connection('SOME_PREFIX_' + request.subdomain)) 
    end 
end 

# Return regular connection hash but with database name changed 
# The database name is a attribute (column in the database) 
def website_connection(subdomain) 
    default_connection.dup.update(:database => subdomain) 
end 

# Regular database.yml configuration hash 
def default_connection 
    @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup 
end 

Questa operazione farà passare a un database come mydb_subdomain. Questa è un'opzione completa per il database di sostituzione, ma rende super semplice la distribuzione di più versioni.

Problemi correlati