New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apartment persists search_path when switching between schemas #133
Comments
we don't clear out the It might be useful to reset the tenant back to the default though on exception. You're welcome to add some tests and submit a pull request if you wanted to see that. |
Thanks for your reply. Unfortunately, the story doesn't end there and I feel there's still a huge security risk here ... In my current project, when switching from an action on a valid tenant to a public action that has no tenant (e.g., a global API call, an action to admin the tenant, i18n management, ...) I'm using no subdomain (i.e., these actions are defined on myapp.com instead of tenant.myapp.com). At this point, the subdomain elevator is still maintaining the last used tenant as its current tenant, prioritizing the previous tenant's search path over the public path, thus accessing tables in a non-public schema instead of the public schema, reading and writing records in the wrong tables. Is this faulty behavior a faulty interpretation and implementation of mine or is this indeed a buggy result of remembering the last tenant of the subdomain elevator? |
We've never encountered this because all of our requests are within the context of a particular tenant. It may be that we should reset the current_tenant if you switch to an invalid one, I'll have to think about that, but normally the way you'd do this is provide a pass through in your middleware. If you inherit from the Apartment Elevator with your own, you can customize it by passing through any non tenanted requests. |
Note that issue #134 points to the same problem. |
I might suggest that the system revert back to "public" or the default tenant if an exception is thrown when a tenant is missing. In my application, tenants may come and go, and switching to a valid tenant is getting blocked when an exception is thrown because of a missing tenant. |
You should be able to revert the connection back to public after every request (please correct me if I'm wrong) by adding this to your elevator: module Apartment
module Elevetors
class MyElevator < Generic
def call(*args)
super
ensure
Tenant.reset
end
end
end
end |
Ya one change I'm going to make to the Generic elevator actually is to use |
@bradrobertson, so you're thinking something like so? # lib/apartment/elevators/generic.rb
def call(env)
. . .
begin
previous = Tenant.current
Tenant.switch! database if database
@app.call(env)
ensure
Tenant.switch!(previous) rescue Tenant.reset
end
end or more along the lines of this: # lib/apartment/adapters/abstract_adapter.rb
def switch!(tenant = nil)
...
end
def switch(tenant = nil, &block)
raise ArgumentError, 'block expected, none given' unless block_given?
previous = current_tenant
begin
switch!(tenant)
block.call
ensure
switch!(previous) rescue reset
end
end
# lib/apartment/elevators/generic.rb
def call(env)
. . .
if database
Tenant.switch(database) { @app.call(env) }
else
@app.call(env)
end
end |
the 2nd one... Since switch with a block does the But the adapter code itself |
Ah - I probably should have looked at the more recent code and I would have seen the newer #switch() that you speak of. But yeah, I grok ya. |
I'm not sure if this is related but I'm seeing a seemingly related issue in my RSpec test suite where roughly 20% of the time (and only on our CI server) a test will fail with:
I have Apartment::Tenant.reset in both before and after hooks (shouldn't be necessary?) and I've tried manually setting the search path (
And on a potentially related note sometimes the failure is that a table doesn't exist, as if the switch into a tenant was not successful. Again, these only happen a minority of the time, and even sporadically within the same seed. |
Ok I think you can ignore my comment, I think those were leftover ajax requests that were executing after the after hook started. I'm guessing both issues were caused by that effect, my suite seems to be way more consistent after accounting for it. |
I'm using Apartment on a Postgres setup. I have noted that when I'm trying to access non-existing schemas, that the reported search_path is dependent on previous tenant switches, persisting some of these tenants (in the search_path?) among user sessions.
In the example below, I try to access a non-existing tenant, giving me the error that this schema is invalid and could not be found in the search_path of the non-existing tenant nor in public.
Next, I access an existing tenant, where Apartment correctly switches to this tenant.
Next, I try to access the non-existing tenant again, but now Apartment reports that it could not find the non-existing tenant in the existing tenant's schema. Apartment shouldn't be trying to access this other tenant when I'm trying to access a faulty one.
The problem popped up when I noticed I could gain access to a tenant when testing for the nil tenant. I'm using the Subdomain elevator and my app should give an error when accessing the nil domain, not give me full access to a previously used tenant.
The text was updated successfully, but these errors were encountered: