2009-10-21 10 views
9

Estoy trabajando en una aplicación de Rails (actualmente 2.3.4) que hace uso de subdominios para aislar sitios de cuenta independientes. Para que quede claro, lo que quiero decir es que foo.mysite.com debe mostrar el contenido de la cuenta foo y bar.mysite.com debe mostrar el contenido de la barra.Rieles: ¿práctica recomendada para las consultas de alcance basadas en el subdominio?

¿Cuál es la mejor manera de asegurarse de que todas las consultas de modelo tengan el alcance del subdominio actual?

Por ejemplo, uno de mis reguladores se ve algo como:

@page = @global_organization.pages.find_by_id(params[:id])   

(Nota @global_organization se encuentra en el application_controller través subdominio-fu.) Cuando lo que preferiría es algo así como:

@page = Page.find_by_id(params[:id]) 

donde los hallazgos del modelo de página se asignan automáticamente a la organización correcta. He intentado usar la directiva default_scope así: (en el modelo de página)

class Page < ActiveRecord::Base 
    default_scope :conditions => "organization_id = #{Thread.current[:organization]}" 
    # yadda yadda 
end 

(Una vez más, sólo para observar, el mismo establece application_controller Thread.current [: organización] a Identificación de la organización para el acceso global. El problema con este enfoque es que el alcance predeterminado se establece en la primera solicitud y nunca cambia en solicitudes posteriores a diferentes subdominios.

tres soluciones aparentes hasta el momento:

1 Use VHosts separadas para cada subdominio y sólo ejecutar diferentes instancias de la aplicación por subdominio (usando mod_rails). Este enfoque no es escalable para esta aplicación.

2 Utilice el enfoque del controlador original anterior. Desafortunadamente, hay un buen número de modelos en la aplicación y muchos de los modelos se eliminan de la organización, por lo que esta notación se vuelve cada vez más engorrosa. Lo que es peor es que esto requiere activamente que los desarrolladores recuerden y apliquen la limitación o arriesguen un problema de seguridad significativo.

3 Utilice a before_filter para restablecer el alcance predeterminado de los modelos en cada solicitud. No estoy seguro sobre el rendimiento alcanzado aquí o cuál es la mejor manera de seleccionar qué modelos actualizar por reqeust.

¿Pensamientos? ¿Alguna otra solución que me falta? Esto parece ser un problema bastante común que tiene que haber una mejor práctica. Toda la información apreciada, ¡gracias!

Respuesta

2

¿Ha intentado definir el default_scope a lambda? El bit lambda que define las opciones se evalúa cada vez que se utiliza el alcance.

class Page < ActiveRecord::Base 
    default_scope lambda do 
    {:conditions => "organization_id = #{Thread.current[:organization]}"} 
    end 
    # yadda yadda 
end 

Es esencialmente hacer su tercera opción, trabajando en conjunto con su magia de filtro anterior. Pero es un poco más agresivo que eso, aprovechando cada hallazgo utilizado en el modelo de página.

Si desea este comportamiento para todos los modelos, puede agregar el valor predeterminado a ActiveRecord :: Base, pero mencione que algunos se encuentran a un par de combinaciones. Por lo tanto, si realiza esta ruta, deberá anular los ámbitos predeterminados en esos modelos para abordar las combinaciones.

+0

Gracias por el aporte! ¿Has probado ese código en la práctica? Acabo de probarlo en la aplicación y, aunque puedo confirmar que efectivamente se está evaluando la lambda en cada búsqueda, el valor predeterminado_scope no aplica los valores devueltos. Mirando el resultado SQL, las condiciones parecen simplemente ignoradas. Por muchas otras razones, aún no he podido arrancar mi depurador, pero voy a hacer un pico tan pronto como vuelva a funcionar. ¿Pensamientos sobre por qué podría no funcionar? – qfinder

+0

Honestamente, no tengo idea de cómo el servidor interno usa subprocesos, por lo que asumí 'Thread.current [: organization]' trabajado en el modelo. Pero no sé si eso explicaría por qué se ignoran las condiciones. Como esencialmente está definiendo el alcance predeterminado para buscar organazation_id = nil. – EmFi

+0

Tras una investigación adicional, parece que esto no funciona todavía. Hay un parche, pero YMMV: https://rails.lighthouseapp.com/projects/8994/tickets/1812-default_scope-cant-take-procs – EmFi

2

Tenga cuidado al ir con el alcance predeterminado aquí, ya que le llevará a una falsa sensación de seguridad, especialmente al crear registros.

Siempre he usado el primer ejemplo de mantener esto en claro:

@page = @go.pages.find(params[:id]) 

La mayor razón se debe a que también querrá asegurarse de esta asociación se aplica a los nuevos registros, por lo que sus nuevas acciones/Crear mirarán como el siguiente, asegurándose de que están adecuadamente como alcance la asociación de padres:

# New 
@page = @go.pages.new 

# Create 
@page = @go.pages.create(params[:page]) 
+0

Buen punto, en este caso resulta que debido a la estructura de unión solo hay unos pocos lugares donde se debe aplicar en las creaciones, pero hay muchos lugares donde se debe aplicar en las lecturas, por lo que aún me gustaría encontrar una manera para usar el default_scope. – qfinder

+0

También, echar un vistazo a http://github.com/devinterface/authlogic_subdomain_fu_startup_app, que es una aplicación de ejemplo para comenzar a utilizar una aplicación multi-arrendatario subdominio con ámbito. Se encarga de la creación de cuentas con el usuario inicial (que también es el propietario de la cuenta). – bensie

+1

Puede parecer un desorden de trabajo/código adicional en el controlador, pero realmente no lo es. Al final, muestra la intención del programador, lo que hará que sea mucho más fácil modificarlo en el futuro. En cuanto a la necesidad de los desarrolladores para recordar y aplicar la limitación, usted debe hacerse pruebas para todas las acciones de todas maneras, y una de esas pruebas deben asegurarse de que el modelo está en el ámbito correctamente a su padre. – bensie

0

que podría ser mejor de tener un database per account and switching the database connection basado en el subdominio.

Además del enlace anterior, si tiene un modelo (en su Cuenta de cuenta) que desea utilizar la base de datos predeterminada, solo incluya establish connection en el modelo.

class Account < ActiveRecord::Base 

    # Always use shared database 
    establish_connection "shared_#{RAILS_ENV}".to_sym 
+0

Puede ser una solución viable, pero hace que los informes en ejecución (análisis) en las cuentas sean un gran problema. También migración, etc. –

+0

Sí, las migraciones fueron un motivo de dolor para mí. Recuerdo haber visto una solución que permite que las migraciones se puedan ejecutar contra una lista de bases de datos, pero no recuerdo dónde. – Kris

Cuestiones relacionadas