Rails - 失去与集成测试和 Capybara 的会话 - CSRF 相关?

发布于 2024-11-18 23:25:38 字数 949 浏览 2 评论 0原文

我正在使用 Rails 3.1.0.rc4,并且正在使用 capybara 的新 Steak-like DSL 和 Rspec(使用 Devise 身份验证)进行集成测试。

我遇到的问题是,当我运行集成测试时,机架来自水豚的测试驱动程序似乎完全丢失了用户登录的会话,事实上,会话似乎完全清除了。

经过几天的调试,我完全不知道为什么。通过逐行检查中间件堆栈,我相信我已经将问题归结为导致此问题的 ActiveRecord::SessionStore 中发生的事情。我在此处读到,如果可以的话,Rails 将清除会话' t 验证 CSRF 令牌,这让我相信我配置了错误,并且由于某种原因,这个测试没有正确验证 CSRF 令牌。

这是我的 /initializers 目录中的 session_store.rb 中的内容:

MyApp::Application.config.session_store :active_record_store

有谁了解 Rails 中的 CSRF 保护,有任何线索可以解释为什么会发生这种情况吗?

另外,这里有一些需要注意的事情:

  • 我试图测试的东西实际上在浏览器本身内工作,只有这一个测试正在删除会话,
  • 在提交操作网址为的表单后,会话似乎被删除到另一台服务器。我在测试中使用 VCR gem 捕获对此外部服务器的请求/响应,虽然我相信我已经将外部请求视为问题,但这可能与 CSRF 令牌未进行身份验证直接相关,从而清除会话。
  • 涉及登录/使用会话的其他测试不会删除会话

任何人都可以给我任何关于这里到底发生了什么的线索,以及为什么一个测试似乎任意删除其会话并在我身上失败?我已经做了很多调试,并尝试了我能想到的一切。

I'm using Rails 3.1.0.rc4 and I'm working on doing integration tests with capybara's new Steak-like DSL and Rspec (using Devise authentication)

The issue I'm having is that when I run an integration test, the rack-test driver from capybara seems to just completely lose the user's logged in session, in fact, the session seems to just clear out altogether.

After days of debugging, I'm at a complete loss as to why. Going line by line through the middleware stack, I believe I've ruled the problem down to something going on in the ActiveRecord::SessionStore that is causing this. I've read here that Rails will clear out a session if it can't validate the CSRF token, which leaves me to believe that I've got something configured wrong, and for some reason this one test is not authenticating the CSRF token correctly.

This is what is in my session_store.rb in the /initializers directory:

MyApp::Application.config.session_store :active_record_store

Does anyone who knows about CSRF protection in rails have any leads on why this may be happening?

Also, here are some things to note:

  • the thing I'm trying to test actually works within the browser itself, only this one test is dropping the session
  • the session seems to get dropped after the submission of a form to which the action url is to another server. I'm using the VCR gem for capturing the requests/responses to this external server in the test, and while I believe I've ruled the external request as the problem, this may have something directly to do with the CSRF token not authenticating, thus clearing out the session.
  • other tests involving logging in / using sessions are not dropping sessions

Can anyone give me any leads as to what is going on here exactly, and why the one test just seems to arbitrarily drop its session and fail on me? I've done lots of debugging and have tried everything I can possible think of.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鲸落 2024-11-25 23:25:38

我也是水豚新手,也遇到了类似的问题。

我试图登录用户做这样的事情:

post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

并且工作正常,直到我尝试用水豚做一些事情,例如访问页面并测试用户是否登录。这个简单的测试没有通过:

visit root_path
page.should have_content("logout") #if the user is logged in then the logout link should be present

首先我以为水豚正在清除会话,但我错了。我花了一些时间才意识到,水豚驱动程序正在使用处理自己的会话,因此,从水豚的角度来看,我的用户从未登录过。为此,您必须这样做

page.driver.post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

不确定是否这是你的情况,但希望有所帮助。

I'm new to capybara too and I was having a similar problem.

I was trying to login a user doing something like this:

post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

And that was working ok until I tried to do something with capybara, such as visiting a page and just testing if the user was logged in. This simple test was not passing:

visit root_path
page.should have_content("logout") #if the user is logged in then the logout link should be present

At first I thought capybara was clearing the sessions but I was wrong. The thing that took me some time to realize is that the driver capybara is using handles its own sessions, so, from the point of view of capybara my user was never logged in. To do so you have to do it like this

page.driver.post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

Not sure if this is your case, but hope that helps.

老子叫无熙 2024-11-25 23:25:38

我可以通过在 config/initializers/test.rb 中将此值设置为 true 来修复此错误

# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = true

之前,CSRF 标记未打印到 中。。改变这个值后,它们终于出现了。

I was able to fix this error by setting this value to true in config/initializers/test.rb

# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = true

Beforehand, the CSRF <meta> tags were not printing out to the <head>. After changing this value they finally appear.

念﹏祤嫣 2024-11-25 23:25:38

手动执行此操作的方法非常简单:

it "does something after login" do
  password = "secretpass"
  user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => user.email
  fill_in "Password", :with => password
  click_button "Log in"
  visit # somewhere else and do something
end

然后您可以将其分解为“spec_helper.rb”中的一个函数:

# at the bottom of 'spec_helper.rb'
def make_user_and_login
  password = "secretpass"
  @user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => @user.email
  fill_in "Password", :with => password
  click_button "Log in"
end

并在任何测试中使用它(可能是请求规范):

it "does something after login" do
  make_user_and_login
  # now test something that requires a logged in user
  # you have access to the @user instance variable
end

The manual way of doing it is very simple:

it "does something after login" do
  password = "secretpass"
  user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => user.email
  fill_in "Password", :with => password
  click_button "Log in"
  visit # somewhere else and do something
end

You can then break this out into a function in your 'spec_helper.rb':

# at the bottom of 'spec_helper.rb'
def make_user_and_login
  password = "secretpass"
  @user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => @user.email
  fill_in "Password", :with => password
  click_button "Log in"
end

and use it in any of your tests (probably request specs):

it "does something after login" do
  make_user_and_login
  # now test something that requires a logged in user
  # you have access to the @user instance variable
end
御弟哥哥 2024-11-25 23:25:38

这可能不太可能,但我相信在 click_button 'Sign in' 并立即调用 visit else 后,我们最终会处于糟糕的状态。

我的理论是,当我们单击按钮时,请求尚未完成,我们通过访问另一条路径来终止它。

来自水豚文档:

向 DSL 发出指令时,例如:

<代码>
点击链接('foo')
click_link('栏')
期望(页面).to have_content('baz')

如果单击 foo 链接触发异步进程,例如 Ajax 请求,该进程完成后会将 bar 链接添加到页面,则单击 bar 链接预计会失败,因为该链接不会还存在。 但是水豚足够聪明,会在放弃并抛出错误之前在短时间内重试查找链接。

如果是这种情况,解决方案很简单:给水豚一些东西来寻找并让它等待,直到请求完成。这可以很简单,只需添加:

expect(page).to have_text('Signed in as [电子邮件受保护]')

This might be a long shot but I believe we end up in a bad state after click_button 'Sign in' and calling visit elsewhere immediately after.

My theory is that when we click the button the request hasn't completed yet, and we kill it by visiting another path.

From the Capybara documentation:

When issuing instructions to the DSL such as:


click_link('foo')
click_link('bar')
expect(page).to have_content('baz')

If clicking on the foo link triggers an asynchronous process, such as an Ajax request, which, when complete will add the bar link to the page, clicking on the bar link would be expected to fail, since that link doesn't exist yet. However Capybara is smart enough to retry finding the link for a brief period of time before giving up and throwing an error.

If this is the case the solution is simple: give Capybara something to look for and let it wait until the request is complete. This can be as simple as adding:

expect(page).to have_text('Signed in as [email protected]')

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文